Eclipse GEF Tutorial: Secondary layout for multiple children of a PolylineConnection with ConnectionLocator and RelativeLocator

In an Eclipse GEF editor I am developing, PolylineConnections can have multiple children (of a type extending org.eclipse.draw2d.Label). I’ve wanted them to be located at ConnectionLocator.MIDDLE, but they should also have a secondary layout, similar to ToolbarLayout (i.e., ordered in a single column, cf. Figure 1).

Simplified depiction of desired layout of figure children of an Eclipse GEF PolylineConnection.

Figure 1: Simplified depiction of desired layout of figure children of an Eclipse GEF PolylineConnection.

Why Two Layouts?

In other words, and as described in my stackoverflow.com question on the topic, “I want to add all label children of a polyline to ConnectionLocator.MIDDLE in a ToolbarLayout…” However, what I don’t want to do is – as suggested by vainolo in his answer, which is of course otherwise perfectly correct – to create a container figure to include all children in a ToolbarLayout and locate this container figure at ConnectionLocator.MIDDLE. This is because the child figures are bound to direct model children of the connection’s model via their EditParts, which must be available via the getModelChildren() method in the respective “PolylineConnectionEditPart“.

Mixing Two Layouts on a GEF Figure: Impossible

The big issue here is that I need to mix two layouts, which is of course impossible: If I add all children at ConnectionLocator.MIDDLE they’ll be drawn on top of one another. And I cannot attach a ToolbarLayout to a ConnectionLocator.

However, as you can see in Figure 1, All of my PolylineConnections have an ID, which is basically also an org.eclipse.draw2d.Label, and should always be displayed as the first child figure. So basically I have two distinct groups of children of the connection: 1) A single Label that exists for all connections, is always the first child of the connection, and is distinct from the other children in that it is the only child of a custom type, say, IDLabel. 2) 0-n children following the IDLabel, of a type extending Label.

RelativeLocator to the Rescue

Luckily, the Draw2D API knows different types implementing the Locator interface, amongst them a wee gem called RelativeLocator. From the API reference:

public class RelativeLocator
extends Object
implements Locator

Places a handle relative to a figure’s bounds. The placement is determined by indicating the figure to which the placement is relative, and two floating-point value indicating the horizontal and vertical offset from that figure’s top-left corner. The values (0.0, 0.0) would indicate the figure’s top-left corner, while the values (1.0, 1.0) would indicate the figure’s bottom-right corner.

The interesting part is displayed in bold: Relative placement to another figure will work for me, as I do know the first child figure in any case, namely the IDLabel.

With RelativeLocator, the whole affair is more a less a piece of cake:

  1. Place the IDLabel at ConnectionLocator.MIDDLE
  2. Place the other child figures relative to the location of the child figure before them, with the first of these child figures placed relative to the IDLabel

Implementation

In my case, the implementation looks roughly as follows. The IDLabel is added to the connection figure in the actual MyPolylineConnection class. In the connection’s EditPart (here, say, MyPolylineConnectionEditPart), the other child figures are added in refreshVisuals().

/**
 * @author New Code on the Block
 *
 */
public class MyPolylineConnection extends PolylineConnection {

	public MyPolylineConnection(String labelString) {
		IDLabel iDLabel = new IDLabel(labelString);
		add(iDLabel, new ConnectionLocator(this, ConnectionLocator.MIDDLE));
	}
}

 

/**
* @author New Code on the Block
*
*/
public class MyPolylineConnectionEditPart extends AbstractConnectionEditPart {

@Override
protected List getModelChildren() {
List childrenList = new ArrayList();
MyConnectionModel model = (MyConnectionModel) getModel();
childrenList.addAll(model.getChildModels());
return childrenList;
}

@Override
protected IFigure createFigure() {
return new MyPolylineConnection(getSomeID());
}

@Override
protected void refreshVisuals() {
MyPolylineConnection figure = (MyPolylineConnection) getFigure();

List

Conclusion

This tutorial shows how to use RelativeLocator in connection with ConnectionLocator to simulate a ToolbarLayout-like layout for multiple children of a GEF Connection. In order for this to work, the first child of the connection must be placed at ConnectionLocator.MIDDLE while the rest of the child figures are placed relative to their predecessor with RelativeLocator.

In my case, the first child is of a different type than the rest, making it easy to find it by using instanceof. I am not making assumptions for the case that all child figures are of the same type, but I guess there will be a way to make this work. I guess the solution would be getting all child figures, and then looping through them, adding the first in the list at ConnectionLocator.MIDDLE and the rest of them at RelativeLocator with the figure at indexOf(currentFigure) - 1 as the figure the placement is relative to.

Please leave a note if you find any issues with my solution, if you have any optimization ideas, or indeed any other comment.

TwitterFacebookGoogle+EmailLinkedInPinterestDiggRedditStumbleUpon
If you found this post helpful, you may want to consider donating to help cover server costs.
Tagged with: , , , , , , , , , , ,
Posted in Draw2D, Eclipse RCP, GEF: Eclipse Graphical Editing Framework, Tutorial

Leave a Comment

%d bloggers like this: