Previous tutorial: Creating an OPM GEF Editor – Part 25: Showing Feedback to the User
Usability is very important when creating user interfaces, adn having to click every time in a different tool in the OPP palette to create different model entities made the experience of creating a new diagram very cumbersome. The diagram canvas is relatively large, and having to move the mouse all the time to the palette took simply too much time. There are some ways to solve this problem, and one of them is using the context menu to enable these tools. As I already showed how we can create actions in the context menu, this was just a problem of how to enable the tools when the action is triggered. As it turned out, this was not very complicated.
First, let’s create all the plumbing: we need to create an Action
, register it in the editor, and add it to the context menu. The OPMCreateObjectAction
is an empty action that just defines the minimum needed for an action, without doing anything:
package com.vainolo.phd.opm.gef.editor.action; import org.eclipse.gef.Request; import org.eclipse.gef.tools.CreationTool; import org.eclipse.gef.ui.actions.WorkbenchPartAction; import org.eclipse.ui.IWorkbenchPart; import com.vainolo.phd.opm.gef.editor.OPMGraphicalEditor; import com.vainolo.phd.opm.gef.editor.factory.OPMObjectFactory; public class OPMCreateObjectAction extends WorkbenchPartAction { public static final String ID = "CreateObject"; public static final String REQUEST = "CreateObject"; private final Request request; private CreationTool tool; public OPMCreateObjectAction(IWorkbenchPart part) { super(part); request = new Request(REQUEST); setId(ID); setText("Create Object"); } public Request getRequest() { return request; } @Override public void run() { } @Override protected boolean calculateEnabled() { return true; } }
Now we add this action to the editor in the OPMGraphicalEditor
class, inside the createActions()
function:
protected void createActions() { super.createActions(); // lots of code up here IAction action = new OPMCreateObjectAction(this); getActionRegistry().registerAction(action); // some more code down here }
And add it to the context menu in the OPMGraphicalEditorContextMenuProvider
class, inside the buildContextMenu()
function:
public void buildContextMenu(IMenuManager menu) { GEFActionConstants.addStandardActionGroups(menu); // lots of code up here IAction action = getActionRegistry().getAction(OPMCreateObjectAction.ID); menu.appendToGroup(GEFActionConstants.GROUP_EDIT, action); // some more code down here… }
You can start your editor now and see that the action is already in the context menu, but clicking on it does nothing (as expected).
Making the tool available from the action turned out to be a simple thing. We just need to create an instance of the tool and set it as the active tool of the editor’s EditDomain
. Inside the run()
function in the OPMCreateObjectAction
class, I added the following code:
public void run() { OPMGraphicalEditor editor = (OPMGraphicalEditor) getWorkbenchPart(); CreationTool tool = new CreationTool(new OPMObjectFactory(editor.getIdManager()));; editor.getEditDomain().setActiveTool(tool); }
And voilà! When you click on the action in the context menu, the tool to create a new Object will be activated! Note that I had to expose the EditDomain
of the editor that is originally protected
to get this done, and this may not be the best practice, but for the first iteration, it works pretty well. There are also some more optimizations we can do, like create the tool once and not every time the action is executed, but I leave this to you as a homework :-).
Enabling the creation of links from the context menu had an added requirements – if the context menu is activated from an element (Object or Process), we should be able to use this element as the source of the link if it is a valid link source. The problem that I found here is that there is no simple way to know what is the point where the context menu was initiated, and getting the mouse location when the action is triggered is not good since this will give us the location of the mouse where the context menu item is located, which may be very different from the original location where the context menu was invoked.
I didn’t really solve this but did a workaround – if an element is selected when the context menu is invoked (and from my tests, when you invoke the context menu on top of an element it gets selected), I take the center point of this element and simulate a right-button mouse down event on the tool. This is how the run()
function looks like in this case:
public void run() { OPMGraphicalEditor editor = (OPMGraphicalEditor) getWorkbenchPart(); editor.getGraphicalViewer().getSelectedEditParts(); editor.getEditDomain().setActiveTool(tool); if(editor.getGraphicalViewer().getSelectedEditParts().size() == 1) { if(!OPMProcessEditPart.class.isInstance(editor.getGraphicalViewer().getSelectedEditParts().get(0))) return; OPMProcessEditPart selection = (OPMProcessEditPart) editor.getGraphicalViewer().getSelectedEditParts().get(0); Event e = new Event(); e.button = 1; e.stateMask = 0; e.widget = editor.getGraphicalViewer().getControl(); Rectangle constraints = ((OPMProcess) selection.getModel()).getConstraints(); e.x = constraints.x + constraints.width / 2; e.y = constraints.y + constraints.height / 2; tool.mouseDown(new MouseEvent(e), editor.getGraphicalViewer()); } }
After having implemented this, I don’t understand why I didn’t do this before. Creating a model takes me half the time now.
As always, you can find the latest and greatest version of the code at Github. For this tutorials, check the OPMCreate*Action
classes in the com.vainolo.phd.opm.gef.editor.action
package of the com.vainolo.phd.opm.gef
project.