Home » Eclipse Projects » Sirius » Problem with custom layout provider
Problem with custom layout provider [message #1743091] |
Mon, 12 September 2016 07:07 |
Martin Jedlicka Messages: 122 Registered: January 2016 |
Senior Member |
|
|
Hello,
I had a question about the order of execution of commands in CompoundCommand.
Implements a custom layout provider (click on the Arrange All), where the need to sequentially execute three actions
1. place the nodes on a specific position and set their size
2. ensure a minimum distance between nodes (protection zone)
3. connection nodes via edges with its own algorithm
I have several problems.
1. Individual commands are called sequentially, but in a graphical editor, it seems that all are executed simultaneously, ie. the animation shows how the nodes move and in same time are connected. This has the effect that it incorrectly operates my algorithm connection nodes because the nodes are not yet in place. How to correctly ensure that the command executed after the end of previous command?
2. Command execution will not always right, respectively. in debug are all the calculations algorithms correctly, but the graphics editor, I see that some changes are implemented only after repeated call Arrange All. How to ensure that the actions execute on the first call?
I have the code below.
Thanks for any advice.
Martin
public class CustomLayoutProvider implements LayoutProvider {
@Override
public AbstractLayoutEditPartProvider getLayoutNodeProvider(IGraphicalEditPart container) {
if (isProcessDiagram(container)) {
if (this.layoutProvider == null) {
this.layoutProvider = new ExecuteLayoutProvider(container);
ArrangeRequest layoutRequest = new ArrangeRequest(ActionIds.ACTION_ARRANGE_ALL);
Animation.markBegin();
container.performRequest(layoutRequest);
// time animation
Animation.run(500);
}
}
return this.layoutProvider;
}
@Override
public boolean isDiagramLayoutProvider() {
return false;
}
@Override
public boolean provides(IGraphicalEditPart container) {
return isProcessDiagram(container);
}
private boolean isProcessDiagram(IGraphicalEditPart container) {
if (container instanceof AbstractDDiagramEditPart) {
AbstractDDiagramEditPart editPart = (AbstractDDiagramEditPart) container;
if (editPart.resolveSemanticElement() instanceof DDiagram) {
DDiagram diagram = (DDiagram) editPart.resolveSemanticElement();
if (diagram.getDescription() != null) {
DiagramDescription diagramDescription = diagram.getDescription();
String name = diagramDescription.getName();
if (name.equals(GraphicalProcessEditor.PROCESS_DIAGRAM_NAME) || name.equals(GraphicalProcessEditor.SUBPROCESS_DIAGRAM_NAME)) {
return true;
}
}
}
}
return false;
}
class ExecuteLayoutProvider extends AbstractLayoutProvider {
private IGraphicalEditPart container;
public ExecuteLayoutProvider(IGraphicalEditPart container) {
this.container = container;
}
@Override
public Command layoutEditParts(List selectedObjects, IAdaptable layoutHint) {
// load data
loadData();
if (selectedObjects.isEmpty()) {
return UnexecutableCommand.INSTANCE;
}
CompoundCommand command = new CompoundCommand();
// 1. command - placing nodes (setting location and size)
final PlacingNodesCommand placingNodesCommand = new PlacingNodesCommand(selectedObjects);
command.add(placingNodesCommand);
// 2. command - protection zone
final ProtectionZoneCommand protectionZoneCommand = new ProtectionZoneCommand(selectedObjects);
command.add(protectionZoneCommand);
// 3. command - routing edge - input left, output right
final RoutingInputOutputCommand routingInputOutputCommand = new RoutingInputOutputCommand(selectedObjects);
command.add(routingInputOutputCommand);
command.execute();
return command;
}
}
class PlacingNodesCommand extends Command {
private List<?> selectedObjects = null;
public PlacingNodesCommand(List<?> selectedObjects) {
super();
this.selectedObjects = selectedObjects;
}
@Override
public void execute() {
// filter all IGraphicalEditPart from the list of selected objects
for (IGraphicalEditPart graphicalEditPart : Iterables.filter(selectedObjects, IGraphicalEditPart.class)) {
View notationView = graphicalEditPart.getNotationView();
// element is a node
if (notationView instanceof Node) {
final Node node = (Node) notationView;
final LayoutConstraint layoutConstraint = node.getLayoutConstraint();
final EObject eObject = node.getElement();
ProcessElement processElement = getProcessElement(eObject);
if (processElement == null) {
logger.error("Process element is null.");
}
// set element - the position and size
setPositionAndSizeElements(layoutConstraint, processElement);
}
// refresh
graphicalEditPart.refresh();
}
}
}
class ProtectionZoneCommand extends Command {
private List<?> selectedObjects = null;
public ProtectionZoneCommand(List<?> selectedObjects) {
super();
this.selectedObjects = selectedObjects;
}
@Override
public void execute() {
setProtectionZone(selectedObjects);
}
}
class RoutingInputOutputCommand extends Command {
private List<?> selectedObjects = null;
public RoutingInputOutputCommand(List<?> selectedObjects) {
super();
this.selectedObjects = selectedObjects;
}
@SuppressWarnings("restriction")
@Override
public void execute() {
// filter all IGraphicalEditPart from the list of selected objects
for (IGraphicalEditPart graphicalEditPart : Iterables.filter(selectedObjects, IGraphicalEditPart.class)) {
View notationView = graphicalEditPart.getNotationView();
// element is an edge - for creating UI edges RelativeBendpoints
if (notationView instanceof Edge) {
final Edge edge = (Edge) notationView;
Bendpoints bendpoints = edge.getBendpoints();
if (bendpoints instanceof RelativeBendpoints) {
RelativeBendpoints relativeBendpoints = (RelativeBendpoints) bendpoints;
// list of new points of edges
List<RelativeBendpoint> newBendpoints = new ArrayList<>();
// get connection - between which the source node and the target node exists edge
Connection connection = (Connection) GMFHelper.getGraphicalEditPart(edge).get().getFigure();
Point sourceRefPoint = connection.getSourceAnchor().getReferencePoint();
Point targetRefPoint = connection.getTargetAnchor().getReferencePoint();
connection.translateToRelative(sourceRefPoint);
connection.translateToRelative(targetRefPoint);
IFigure sourceFigure = connection.getSourceAnchor().getOwner();
IFigure targetFigure = connection.getTargetAnchor().getOwner();
List<?> points = relativeBendpoints.getPoints();
// start point
...
// target point
...
relativeBendpoints.setPoints(newlist);
}
// refresh
graphicalEditPart.refresh();
}
}
}
|
|
|
Re: Problem with custom layout provider [message #1743093 is a reply to message #1743091] |
Mon, 12 September 2016 08:08 |
|
Hi.
In your implementation of "Command layoutEditParts(List selectedObjects, IAdaptable layoutHints)", I see that you execute the command yourself (by calling command.execute()), while you should only *build* the Command and return it to the framework, which will execute it at the appropriate time. I see the same pattern in getLayoutNodeProvider(), which is supposed to only return a layout provider, that will be invoked later; it should not execute the layout commands (which is triggered by the call to container.performRequest(layoutRequest)) in getLayoutNodeProvider() itself.
The Javadoc in GMF's AbstractLayoutEditPartProvider.layoutEditParts(GraphicalEditPart, IAdaptable) is a little confusing, as it says "Layout the objects in this container using the specified layout type.", but basically whenever a method in GMF returns a Command, it should only build/configure and return the Command object, and the framework will take care of invoking it (maybe with others) at a later time.
Regards,
Pierre-Charles
--
Need training or professional services for Sirius?
http://www.obeodesigner.com/sirius
Pierre-Charles David - Obeo
Need training or professional services for Sirius?
http://www.obeodesigner.com/sirius
|
|
| | |
Re: Problem with custom layout provider [message #1743307 is a reply to message #1743302] |
Wed, 14 September 2016 09:04 |
|
Quote:
I need to move the nodes that are too near each other and then connect those nodes.
I'm not sure I get this part. A layout provider can not create new connections, it can only move (or resize) things around, create/remove/move bendpoints etc.
Quote:
I need to ensure that the first nodes sequentially moved, and only then interconnected. It happens to me, however, that the move and interconnection is performed at the same time and that was often not guarantee correctly connected, because the individual does not fit RelativeBendpoint.
Note sure this is the cause of your issue, but if you write your layout algorithm as multiple successive steps/commands, you need to take into account the fact that except for the very first command in the sequence, the commands you create will see a different, already modified, state of the model. For example if you start with state S0, and create commands C1, C2:
* C1 will be executed on S0, producing a state S1;
* C2 will be executed the, on S1, producing a state S2;
So by the time you are *creating* C2, you must be aware that the state you see (S0 at that time) is not the one it will see when executed. You must create C2 in a way that compensate that when it will execute, C1 will already have happened.
Pierre-Charles David - Obeo
Need training or professional services for Sirius?
http://www.obeodesigner.com/sirius
|
|
| | |
Re: Problem with custom layout provider [message #1743535 is a reply to message #1743505] |
Fri, 16 September 2016 09:15 |
|
Hi.
The organisation of your code in successive commands have no direct relation with the layout animations.
The model changes performed by the command are one thing, the visual presentation of the changes is another. However you structure your commands, they will always be executed as one single operation on the underlying EMF/GMF/Sirius models. Only then are the Draw2D figures updated according to the final state (and thus layout). See the javadoc of the Animation class you use in your example:
* A utility for coordinating figure animations. During animation, multiple
* <i>animators</i> are employed to capture the <em>initial</em> and
* <em>final</em> states for one or more figures. The animators then playback
* the animation by interpolating the intermediate states for each figure using
* the initial and final "keyframes".
Whatever intermediate state your models passed through during the execution of the Command are completely invisible to the graphical layer (Draw2D). The only thing it sees are the original state (position, size etc.) of the figures, and the updated state according to the final model state (the initial and final keyframes). Animator then does simple interpolation between those (see org.eclipse.draw2d.LayoutAnimator.playback(IFigure) for example).
If you need a different kind of animation, the changes you need to make are not at the Command level, but at the Drawd2D Animation/Animator level. This has nothing to do with Sirius itself and you will have a better chance of an answer in the GEF forum.
Regards,
Pierre-Charles
--
Need training or professional services for Sirius?
http://www.obeodesigner.com/sirius
Pierre-Charles David - Obeo
Need training or professional services for Sirius?
http://www.obeodesigner.com/sirius
|
|
| |
Goto Forum:
Current Time: Fri Dec 27 01:33:16 GMT 2024
Powered by FUDForum. Page generated in 0.06299 seconds
|