Layouts are used to arrange shapes on diagrams or within shape compartments. A layout can be triggered for the current selection, or for all children within a container.
The layout service allows for the arrangement of diagram elements for a given layout type.
To contribute a layout provider to the service extend the
org.eclipse.gmf.runtime.diagram.ui.layoutProviders extension-point and set
the layoutProvider class to be a class which extends
org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider.
<extension
point="org.eclipse.gmf.runtime.diagram.ui.layoutProviders">
<layoutProvider class="com.ibm.examples.providers.MyLayoutProvider">
<Priority name="Medium"/>
</layoutProvider>
</extension>
GMF provides a set of layout classes for the layout provider to extend:
AbstractLayoutNodeProviderLeftRightProviderTopDownProviderCompositeLeftRightProviderCompositeTopDownProvider
In this example the MyLayoutProvider class extends
AbstractLayoutNodeProvider in order to provide a customized layout algorithm.
MyLayoutProvider implements the provides method such that
it provides for only the layout type myLayoutType. To override the default layout behaviour,
the provider should provide for the
LayoutType.DEFAULT layout type from GMF.
Override the
layoutLayoutNodes method and return a Runnable which will execute
the custom layout algorithm on the given list of nodes. In this example, the layout algorithm
will layout the nodes in a pyramid pattern.
public class MyLayoutProvider extends AbstractLayoutNodeProvider {
public static final String LAYOUT_TYPE = "myLayoutType";
public boolean provides(IOperation operation) {
View cview = getContainer(operation);
if (cview == null)
return false;
IAdaptable layoutHint = ((ILayoutNodeOperation) operation).getLayoutHint();
String layoutType = (String) layoutHint.getAdapter(String.class);
return LAYOUT_TYPE.equals(layoutType);
}
/**
* Layout nodes in a pyramid shape
* o
* o o
* o o o
* Last row may not be completely filled, depending on number
* of nodes to arrange.
*/
public Runnable layoutLayoutNodes(final List layoutNodes,
final boolean offsetFromBoundingBox, final IAdaptable layoutHint) {
return new Runnable() {
public void run() {
// calculate the grid size
int gridWidth = 0;
int gridHeight = 0;
ListIterator li = layoutNodes.listIterator();
while (li.hasNext()) {
ILayoutNode lnode = (ILayoutNode)li.next();
if (lnode.getWidth() > gridWidth)
gridWidth = lnode.getWidth();
if (lnode.getHeight() > gridHeight)
gridHeight = lnode.getHeight();
}
// add a small buffer in HiMetric units
gridWidth += 100;
gridHeight += 100;
// determine number of rows
int rowsize = (int)Math.floor(Math.sqrt(layoutNodes.size() * 2));
int boxXOffset = 1000;
int boxYOffset = 1000;
// set node bounds
li = layoutNodes.listIterator();
for (int i = 1; i <= rowsize; ++i) {
int xoffset = (rowsize - i) * gridWidth + boxYOffset;
int yoffset = (i - 1) * gridHeight + boxXOffset;
for (int j = 1; j <= i && li.hasNext(); ++j, xoffset += (gridWidth * 2)) {
ILayoutNode lnode = (ILayoutNode)li.next();
Bounds bounds = (Bounds)lnode.getNode().getLayoutConstraint();
bounds.setX(xoffset);
bounds.setY(yoffset);
lnode.getNode().setLayoutConstraint(bounds);
}
}
}
};
}
}
The LayoutService can now be queried for this custom layout provider to layout a list of nodes,
or the children of a container.

Instead of querying the
LayoutService directly,
the ArrangeRequest can be used to obtain a command to arrange a set of
EditPart objects. To obtain the layout command construct an
ArrangeRequest and query the
EditPart#getCommand(Request request) method on the container edit part.
The ContainerEditPolicy understands the arrange request and will return a layout command.
This example will make use of a menu bar action to invoke laying out the nodes within a container.
Start by defining the
org.eclipse.ui.actionSets extension to create a new menu action.
<extension
point="org.eclipse.ui.actionSets">
<actionSet
id="com.ibm.example.actionSet"
label="My Layout Action Set"
visible="true">
<menu
label="My Layout"
id="myLayoutMenu">
</menu>
<action
label="Run My Layout"
tooltip="Run My Layout"
class="com.ibm.examples.actions.MyLayoutActionDelegate"
menubarPath="myLayoutMenu/additions"
id="com.ibm.examples.actions.MyLayoutActionDelegate">
</action>
</actionSet>
</extension>
The ContainerEditPolicy understands
ArrangeRequests of type
ActionId.ACTION_ARRANGE_ALL and
ActionId.ACTION_ARRANGE_SELECTION.
ActionId.ACTION_ARRANGE_ALL is used to layout all children within a container.
This example action delegate demonstrates how to construct the arrange request and query
the container edit part for the layout command.
Calculation for enablement of this action is done by ensuring that the parent of the first selected
object is either the diagram or a shape compartment, whose layout manager is an instance
of
XYLayout because the
XYLayout allows redefining the layout based on X / Y coordinates.
Construct the ArrangeRequest with the
ActionId.ACTION_ARRANGE_ALL request type and the layout type of choice
(this example will invoke the layout created
above). Then query the getCommand method on the parent
diagram or shape compartment edit part.
A neat visual enhancement is to utilize the
Animation class to animate the layout. Before executing the layout command, call
Animation.markBegin() to mark the beginning of the animation process. Then after
executing the layout command run the animation by calling
Animation.run() or optionally
Animation.run(int duration).
public class MyLayoutActionDelegate
extends AbstractActionDelegate implements IWorkbenchWindowActionDelegate {
public static final String LAYOUT_TYPE = "myLayoutType";
protected void doRun(IProgressMonitor progressMonitor) {
EditPart parent = getParentEditPart();
ArrangeRequest request = new ArrangeRequest(ActionIds.ACTION_ARRANGE_ALL, LAYOUT_TYPE);
Command command = parent.getCommand(request);
Animation.markBegin();
command.execute();
Animation.run(500);
}
public void selectionChanged(IAction act, ISelection selection) {
super.selectionChanged(act, selection);
// calculate enabled
if (getStructuredSelection().isEmpty()) {
getAction().setEnabled(false);
return;
}
EditPart parent = getParentEditPart();
if (parent == null || !(parent.getContentPane().getLayoutManager() instanceof XYLayout)) {
getAction().setEnabled(false);
}
getAction().setEnabled(true);
}
protected EditPart getParentEditPart() {
EditPart parent = (EditPart)((IAdaptable)getStructuredSelection().
getFirstElement()).getAdapter(EditPart.class);
while (parent != null && !(parent instanceof DiagramEditPart) && !(parent instanceof ShapeCompartmentEditPart)) {
parent = parent.getParent();
}
return parent;
}
}
ActionId.ACTION_ARRANGE_SELECTION is used to layout a subset of children within a container.
This example action delegate demonstrates how to construct the arrange request and query
the container edit part for the layout command.
Calculation for enablement of this action is done by ensuring that at least 2 or more
non-connection edit parts are selected, and that these selected elements share a common parent
whose layout manager is an instance
of
XYLayout.
ToolUtilities.getSelectionWithoutDependants(List selectedParts) is utilized to
obtain a list containing the top level selected edit parts based on the passed in selection list.
Construct the
ArrangeRequest with the
ActionId.ACTION_ARRANGE_SELECTION request type and the layout type of choice
(this example will invoke the layout created
above) and set the list of edit parts to arrange in the request using
ArrangeRequest#setPartsToArrange(List ep).
Then query the getCommand method on the common parent edit part.
public class MyLayoutActionDelegate
extends AbstractActionDelegate implements IWorkbenchWindowActionDelegate {
public static final String LAYOUT_TYPE = "myLayoutType";
protected void doRun(IProgressMonitor progressMonitor) {
List editParts = new ArrayList();
List selection = ToolUtilities.getSelectionWithoutDependants(
getStructuredSelection().toList());
for (Iterator i = selection.iterator(); i.hasNext(); ) {
Object next = i.next();
if (!(next instanceof ConnectionEditPart) && next instanceof EditPart) {
editParts.add(next);
}
}
ArrangeRequest request = new ArrangeRequest(ActionIds.ACTION_ARRANGE_SELECTION, LAYOUT_TYPE);
request.setPartsToArrange(editParts);
Command command = ((EditPart)editParts.get(0)).getParent().getCommand(request);
Animation.markBegin();
command.execute();
Animation.run(500);
}
public void selectionChanged(IAction act, ISelection selection) {
super.selectionChanged(act, selection);
// calculate enabled
if (getStructuredSelection().isEmpty()) {
getAction().setEnabled(false);
return;
}
List newSelection = ToolUtilities.getSelectionWithoutDependants(
getStructuredSelection().toList());
if (newSelection.size() < 2) {
getAction().setEnabled(false);
return;
}
List editParts = new ArrayList();
EditPart parent = null;
for (Iterator i = newSelection.iterator(); i.hasNext(); ) {
Object next = i.next();
if (!(next instanceof ConnectionEditPart) && next instanceof EditPart) {
if (parent == null) {
parent = ((EditPart)next).getParent();
if (!(parent instanceof IGraphicalEditPart)
|| !(((IGraphicalEditPart)parent).getContentPane().getLayoutManager() instanceof XYLayout)) {
getAction().setEnabled(false);
return;
}
} else if (parent != ((EditPart)next).getParent()) {
getAction().setEnabled(false);
return;
}
editParts.add(next);
}
}
getAction().setEnabled(editParts.size() > 1);
}
}