From 4ce2602c2b75b9553b969d5b038b75fc62865a18 Mon Sep 17 00:00:00 2001 From: Axel RICHARD Date: Wed, 11 Jan 2023 17:21:49 +0100 Subject: [PATCH] [1574] Add support of Edges as targets of single click tools Bug: https://github.com/eclipse-sirius/sirius-components/issues/1574 Signed-off-by: Axel RICHARD --- CHANGELOG.adoc | 2 + .../CompatibilityToolSectionsProvider.java | 23 +- .../services/diagrams/ToolProvider.java | 68 ++- .../handlers/ArrangeAllEventHandler.java | 3 +- ...ClickOnDiagramElementToolEventHandler.java | 49 +- .../main/resources/schema/diagram.graphqls | 11 +- ...OnDiagramElementToolEventHandlerTests.java | 448 +++++++++++++++++- .../diagrams/description/EdgeDescription.java | 5 +- .../IDiagramElementDescription.java | 26 + .../diagrams/description/NodeDescription.java | 5 +- .../SingleClickOnDiagramElementTool.java | 12 +- .../DiagramRepresentation.types.ts | 10 +- .../ViewDiagramDescriptionConverter.java | 7 +- .../emf/diagram/ViewToolSectionsProvider.java | 29 +- 14 files changed, 621 insertions(+), 77 deletions(-) create mode 100644 packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/description/IDiagramElementDescription.java diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 18898538b8..cf47f83c2c 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -10,6 +10,7 @@ === Breaking changes - [core] Records are now used as the implementations of `IInput` +- https://github.com/eclipse-sirius/sirius-components/issues/1574[#1574] [diagram] In `diagram.graphqls`, `SingleClickOnDiagramElementTool` member's `targetDescriptions` is now of new type `DiagramElementDescription` instead of `NodeDescription`. === Dependency update @@ -24,6 +25,7 @@ - https://github.com/eclipse-sirius/sirius-components/issues/1559[#1559] [view] It is now possible to specify the (computed) width and height separately for a _Node Style_ (instead of a single size before, which always resulted in square shapes). - https://github.com/eclipse-sirius/sirius-components/issues/1560[#1560] Remove unused `EditingContextCompletionProposalsDataFetcher` - https://github.com/eclipse-sirius/sirius-components/issues/1426[#1426] [view] Add missing data type on initialDirectEditLabelExpression +- https://github.com/eclipse-sirius/sirius-components/issues/1574[#1574] [diagram] Single click tools can now be executed on Edges in addition to Nodes. == v2023.1.0 diff --git a/packages/compatibility/backend/sirius-components-compatibility/src/main/java/org/eclipse/sirius/components/compatibility/services/diagrams/CompatibilityToolSectionsProvider.java b/packages/compatibility/backend/sirius-components-compatibility/src/main/java/org/eclipse/sirius/components/compatibility/services/diagrams/CompatibilityToolSectionsProvider.java index f81b7dac96..b0e6ee1759 100644 --- a/packages/compatibility/backend/sirius-components-compatibility/src/main/java/org/eclipse/sirius/components/compatibility/services/diagrams/CompatibilityToolSectionsProvider.java +++ b/packages/compatibility/backend/sirius-components-compatibility/src/main/java/org/eclipse/sirius/components/compatibility/services/diagrams/CompatibilityToolSectionsProvider.java @@ -32,6 +32,7 @@ import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.sirius.components.diagrams.description.DiagramDescription; import org.eclipse.sirius.components.diagrams.description.EdgeDescription; +import org.eclipse.sirius.components.diagrams.description.IDiagramElementDescription; import org.eclipse.sirius.components.diagrams.description.NodeDescription; import org.eclipse.sirius.components.diagrams.description.SynchronizationPolicy; import org.eclipse.sirius.components.diagrams.tools.ITool; @@ -258,15 +259,23 @@ private boolean checkPrecondition(Object targetElement, Object diagramElement, A private List createExtraToolSections(Object diagramElementDescription) { List extraToolSections = new ArrayList<>(); - List targetDescriptions = new ArrayList<>(); + List targetDescriptions = new ArrayList<>(); boolean unsynchronizedMapping = false; //@formatter:off - if (diagramElementDescription instanceof NodeDescription) { - targetDescriptions.add((NodeDescription) diagramElementDescription); - unsynchronizedMapping = SynchronizationPolicy.UNSYNCHRONIZED.equals(((NodeDescription) diagramElementDescription).getSynchronizationPolicy()); - } else if (diagramElementDescription instanceof EdgeDescription) { - EdgeDescription edgeDescription = (EdgeDescription) diagramElementDescription; - targetDescriptions.addAll(edgeDescription.getSourceNodeDescriptions()); + if (diagramElementDescription instanceof NodeDescription nodeDescription) { + targetDescriptions.add(nodeDescription); + unsynchronizedMapping = SynchronizationPolicy.UNSYNCHRONIZED.equals(nodeDescription.getSynchronizationPolicy()); + } else if (diagramElementDescription instanceof EdgeDescription edgeDescription) { + var optionalDiagramElementMapping = this.getDiagramElementMapping(edgeDescription); + var domainEdgeMapping = optionalDiagramElementMapping + .filter(EdgeMapping.class::isInstance) + .map(EdgeMapping.class::cast) + .filter(edgeMapping -> edgeMapping.isUseDomainElement()); + if (domainEdgeMapping.isPresent()) { + targetDescriptions.add(edgeDescription); + } else { + targetDescriptions.addAll(edgeDescription.getSourceNodeDescriptions()); + } unsynchronizedMapping = SynchronizationPolicy.UNSYNCHRONIZED.equals(((EdgeDescription) diagramElementDescription).getSynchronizationPolicy()); } diff --git a/packages/compatibility/backend/sirius-components-compatibility/src/main/java/org/eclipse/sirius/components/compatibility/services/diagrams/ToolProvider.java b/packages/compatibility/backend/sirius-components-compatibility/src/main/java/org/eclipse/sirius/components/compatibility/services/diagrams/ToolProvider.java index c3fc75e1e1..8321ce7adf 100644 --- a/packages/compatibility/backend/sirius-components-compatibility/src/main/java/org/eclipse/sirius/components/compatibility/services/diagrams/ToolProvider.java +++ b/packages/compatibility/backend/sirius-components-compatibility/src/main/java/org/eclipse/sirius/components/compatibility/services/diagrams/ToolProvider.java @@ -32,6 +32,7 @@ import org.eclipse.sirius.components.diagrams.Diagram; import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.sirius.components.diagrams.description.EdgeDescription; +import org.eclipse.sirius.components.diagrams.description.IDiagramElementDescription; import org.eclipse.sirius.components.diagrams.description.NodeDescription; import org.eclipse.sirius.components.diagrams.tools.ITool; import org.eclipse.sirius.components.diagrams.tools.SingleClickOnDiagramElementTool; @@ -138,7 +139,7 @@ public List getToolSections(Map id2NodeDescr // @formatter:off List tools = this.getToolDescriptions(siriusToolSection).stream() .filter(this::isSupported) - .map(toolDescription -> this.convertTool(id2NodeDescriptions, siriusDiagramDescription, toolDescription, interpreter)) + .map(toolDescription -> this.convertTool(id2NodeDescriptions, edgeDescriptions, siriusDiagramDescription, toolDescription, interpreter)) .flatMap(Optional::stream) .toList(); // @formatter:on @@ -190,7 +191,7 @@ private List getToolDescriptions(org.eclipse.sirius.dia //@formatter:on } - private List getParentNodeDescriptions(List nodeMappings, Map id2NodeDescriptions) { + private List getParentNodeDescriptions(List nodeMappings, Map id2NodeDescriptions) { //@formatter:off return nodeMappings.stream() .map(AbstractNodeMapping::eContainer) @@ -199,6 +200,9 @@ private List getParentNodeDescriptions(List nodeMa //@formatter:on } - private Optional convertTool(Map id2NodeDescriptions, org.eclipse.sirius.diagram.description.DiagramDescription siriusDiagramDescription, - AbstractToolDescription siriusTool, AQLInterpreter interpreter) { + private Optional convertTool(Map id2NodeDescriptions, List edgeDescriptions, + org.eclipse.sirius.diagram.description.DiagramDescription siriusDiagramDescription, AbstractToolDescription siriusTool, AQLInterpreter interpreter) { Optional result = Optional.empty(); if (siriusTool instanceof NodeCreationDescription) { NodeCreationDescription nodeCreationTool = (NodeCreationDescription) siriusTool; @@ -222,7 +226,7 @@ private Optional convertTool(Map id2NodeDescriptio result = Optional.of(this.convertContainerCreationDescription(id2NodeDescriptions, interpreter, containerCreationDescription)); } else if (siriusTool instanceof org.eclipse.sirius.viewpoint.description.tool.ToolDescription) { org.eclipse.sirius.viewpoint.description.tool.ToolDescription toolDescription = (org.eclipse.sirius.viewpoint.description.tool.ToolDescription) siriusTool; - result = Optional.of(this.convertToolDescription(id2NodeDescriptions, interpreter, siriusDiagramDescription, toolDescription)); + result = Optional.of(this.convertToolDescription(id2NodeDescriptions, edgeDescriptions, interpreter, siriusDiagramDescription, toolDescription)); } else if (siriusTool instanceof EdgeCreationDescription) { EdgeCreationDescription edgeCreationDescription = (EdgeCreationDescription) siriusTool; result = Optional.of(this.convertEdgeCreationDescription(id2NodeDescriptions, interpreter, edgeCreationDescription)); @@ -231,7 +235,7 @@ private Optional convertTool(Map id2NodeDescriptio result = Optional.of(this.convertDeleteElementDescription(id2NodeDescriptions, interpreter, deleteElementDescription)); } else if (siriusTool instanceof OperationAction) { OperationAction operationAction = (OperationAction) siriusTool; - result = Optional.of(this.convertOperationAction(id2NodeDescriptions, interpreter, siriusDiagramDescription, operationAction)); + result = Optional.of(this.convertOperationAction(id2NodeDescriptions, edgeDescriptions, interpreter, siriusDiagramDescription, operationAction)); } return result; @@ -241,7 +245,7 @@ private SingleClickOnDiagramElementTool convertNodeCreationDescription(Map targetDescriptions = this.getParentNodeDescriptions(nodeCreationTool.getNodeMappings(), id2NodeDescriptions); + List targetDescriptions = this.getParentNodeDescriptions(nodeCreationTool.getNodeMappings(), id2NodeDescriptions); var selectModelElementVariableOpt = new SelectModelElementVariableProvider().getSelectModelElementVariable(nodeCreationTool.getVariable()); String selectionDescriptionId = null; if (selectModelElementVariableOpt.isPresent()) { @@ -264,7 +268,7 @@ private SingleClickOnDiagramElementTool convertContainerCreationDescription(Map< String id = this.identifierProvider.getIdentifier(containerCreationDescription); String label = new IdentifiedElementQuery(containerCreationDescription).getLabel(); String imagePath = this.toolImageProvider.getImage(containerCreationDescription); - List targetDescriptions = this.getParentNodeDescriptions(containerCreationDescription.getContainerMappings(), id2NodeDescriptions); + List targetDescriptions = this.getParentNodeDescriptions(containerCreationDescription.getContainerMappings(), id2NodeDescriptions); var selectModelElementVariableOpt = new SelectModelElementVariableProvider().getSelectModelElementVariable(containerCreationDescription.getVariable()); String selectionDescriptionId = null; if (selectModelElementVariableOpt.isPresent()) { @@ -282,8 +286,8 @@ private SingleClickOnDiagramElementTool convertContainerCreationDescription(Map< // @formatter:on } - private SingleClickOnDiagramElementTool convertToolDescription(Map id2NodeDescriptions, AQLInterpreter interpreter, DiagramDescription siriusDiagramDescription, - ToolDescription toolDescription) { + private SingleClickOnDiagramElementTool convertToolDescription(Map id2NodeDescriptions, List edgeDescriptions, AQLInterpreter interpreter, + DiagramDescription siriusDiagramDescription, ToolDescription toolDescription) { String id = this.identifierProvider.getIdentifier(toolDescription); String label = new IdentifiedElementQuery(toolDescription).getLabel(); String imagePath = this.toolImageProvider.getImage(toolDescription); @@ -294,10 +298,25 @@ private SingleClickOnDiagramElementTool convertToolDescription(Map targetDescriptionIds = mappings.stream() .map(this.identifierProvider::getIdentifier) .toList(); - List targetDescriptions = targetDescriptionIds.stream() + + List targetNodeDescriptions = targetDescriptionIds.stream() .map(UUID::fromString) .map(id2NodeDescriptions::get) + .filter(Objects::nonNull) + .filter(IDiagramElementDescription.class::isInstance) + .map(IDiagramElementDescription.class::cast) .toList(); + + List targetEdgeDescriptions = targetDescriptionIds.stream() + .map(UUID::fromString) + .flatMap(targetDescriptionUUID -> edgeDescriptions.stream().filter(edgeDescription -> edgeDescription.getId().equals(targetDescriptionUUID))) + .filter(Objects::nonNull) + .filter(IDiagramElementDescription.class::isInstance) + .map(IDiagramElementDescription.class::cast) + .toList(); + + List targetDescriptions = Stream.concat(targetNodeDescriptions.stream(), targetEdgeDescriptions.stream()).toList(); + return SingleClickOnDiagramElementTool.newSingleClickOnDiagramElementTool(id) .label(label) .imageURL(imagePath) @@ -308,8 +327,8 @@ private SingleClickOnDiagramElementTool convertToolDescription(Map id2NodeDescriptions, AQLInterpreter interpreter, DiagramDescription siriusDiagramDescription, - OperationAction operationAction) { + private SingleClickOnDiagramElementTool convertOperationAction(Map id2NodeDescriptions, List edgeDescriptions, AQLInterpreter interpreter, + DiagramDescription siriusDiagramDescription, OperationAction operationAction) { String id = this.identifierProvider.getIdentifier(operationAction); String label = new IdentifiedElementQuery(operationAction).getLabel(); String imagePath = this.toolImageProvider.getImage(operationAction); @@ -320,10 +339,25 @@ private SingleClickOnDiagramElementTool convertOperationAction(Map targetDescriptionIds = mappings.stream() .map(this.identifierProvider::getIdentifier) .toList(); - List targetDescriptions = targetDescriptionIds.stream() + + List targetNodeDescriptions = targetDescriptionIds.stream() .map(UUID::fromString) .map(id2NodeDescriptions::get) + .filter(Objects::nonNull) + .filter(IDiagramElementDescription.class::isInstance) + .map(IDiagramElementDescription.class::cast) + .toList(); + + List targetEdgeDescriptions = targetDescriptionIds.stream() + .map(UUID::fromString) + .flatMap(targetDescriptionUUID -> edgeDescriptions.stream().filter(edgeDescription -> edgeDescription.getId().equals(targetDescriptionUUID))) + .filter(Objects::nonNull) + .filter(IDiagramElementDescription.class::isInstance) + .map(IDiagramElementDescription.class::cast) .toList(); + + List targetDescriptions = Stream.concat(targetNodeDescriptions.stream(), targetEdgeDescriptions.stream()).toList(); + return SingleClickOnDiagramElementTool.newSingleClickOnDiagramElementTool(id) .label(label) .imageURL(imagePath) @@ -336,10 +370,12 @@ private SingleClickOnDiagramElementTool convertOperationAction(Map getAllDiagramElementMappings(DiagramDescription siriusDiagramDescription) { List mappings = new ArrayList<>(siriusDiagramDescription.getDefaultLayer().getNodeMappings()); + mappings.addAll(siriusDiagramDescription.getDefaultLayer().getEdgeMappings()); mappings.addAll(siriusDiagramDescription.getDefaultLayer().getContainerMappings()); mappings.addAll(this.getAllSubMappings(siriusDiagramDescription.getDefaultLayer().getContainerMappings())); for (AdditionalLayer additionalLayer : siriusDiagramDescription.getAdditionalLayers()) { mappings.addAll(additionalLayer.getNodeMappings()); + mappings.addAll(additionalLayer.getEdgeMappings()); mappings.addAll(additionalLayer.getContainerMappings()); mappings.addAll(this.getAllSubMappings(additionalLayer.getContainerMappings())); } @@ -404,10 +440,12 @@ private SingleClickOnDiagramElementTool convertDeleteElementDescription(Map targetDescriptionIds = mappings.stream() .map(this.identifierProvider::getIdentifier) .toList(); - List targetDescriptions = targetDescriptionIds.stream() + List targetDescriptions = targetDescriptionIds.stream() .map(UUID::fromString) .map(id2NodeDescriptions::get) .filter(Objects::nonNull) + .filter(IDiagramElementDescription.class::isInstance) + .map(IDiagramElementDescription.class::cast) .toList(); return SingleClickOnDiagramElementTool.newSingleClickOnDiagramElementTool(id) .label(label) diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/handlers/ArrangeAllEventHandler.java b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/handlers/ArrangeAllEventHandler.java index 5dc7e82a15..9cebff27d7 100644 --- a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/handlers/ArrangeAllEventHandler.java +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/handlers/ArrangeAllEventHandler.java @@ -23,7 +23,6 @@ import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramInput; import org.eclipse.sirius.components.collaborative.diagrams.dto.ArrangeAllInput; import org.eclipse.sirius.components.collaborative.diagrams.dto.ArrangeAllSuccessPayload; -import org.eclipse.sirius.components.collaborative.diagrams.dto.InvokeSingleClickOnDiagramElementToolInput; import org.eclipse.sirius.components.collaborative.diagrams.messages.ICollaborativeDiagramMessageService; import org.eclipse.sirius.components.core.api.ErrorPayload; import org.eclipse.sirius.components.core.api.IEditingContext; @@ -67,7 +66,7 @@ public boolean canHandle(IDiagramInput diagramInput) { public void handle(One payloadSink, Many changeDescriptionSink, IEditingContext editingContext, IDiagramContext diagramContext, IDiagramInput diagramInput) { this.counter.increment(); - String message = this.messageService.invalidInput(diagramInput.getClass().getSimpleName(), InvokeSingleClickOnDiagramElementToolInput.class.getSimpleName()); + String message = this.messageService.invalidInput(diagramInput.getClass().getSimpleName(), ArrangeAllInput.class.getSimpleName()); IPayload payload = new ErrorPayload(diagramInput.id(), message); ChangeDescription changeDescription = new ChangeDescription(ChangeKind.NOTHING, diagramInput.representationId(), diagramInput); diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/handlers/InvokeSingleClickOnDiagramElementToolEventHandler.java b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/handlers/InvokeSingleClickOnDiagramElementToolEventHandler.java index ee290c1df6..e7529e205e 100644 --- a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/handlers/InvokeSingleClickOnDiagramElementToolEventHandler.java +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/handlers/InvokeSingleClickOnDiagramElementToolEventHandler.java @@ -33,6 +33,7 @@ import org.eclipse.sirius.components.core.api.IPayload; import org.eclipse.sirius.components.core.api.IRepresentationDescriptionSearchService; import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.diagrams.Edge; import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.sirius.components.diagrams.Position; import org.eclipse.sirius.components.diagrams.events.SinglePositionEvent; @@ -135,25 +136,17 @@ private IStatus executeTool(IEditingContext editingContext, IDiagramContext diag IStatus result = new Failure(""); Diagram diagram = diagramContext.getDiagram(); Optional node = this.diagramQueryService.findNodeById(diagram, diagramElementId); - Optional self = Optional.empty(); - if (node.isPresent()) { - self = this.objectService.getObject(editingContext, node.get().getTargetObjectId()); - } else if (Objects.equals(diagram.getId(), diagramElementId)) { - self = this.objectService.getObject(editingContext, diagram.getTargetObjectId()); - } else { - this.logger.warn("The node creation tool {0} cannot be applied on the current diagram {1} and editing context {2}", tool.getId(), diagram.getId(), editingContext.getId()); + Optional edge = Optional.empty(); + if (node.isEmpty()) { + // may be the tool applies on an Edge + edge = this.diagramQueryService.findEdgeById(diagram, diagramElementId); } + Optional self = this.getCurrentContext(editingContext, diagramElementId, tool, diagram, node, edge); // Else, cannot find the node with the given optionalDiagramElementId if (self.isPresent()) { - VariableManager variableManager = new VariableManager(); - variableManager.put(IDiagramContext.DIAGRAM_CONTEXT, diagramContext); - variableManager.put(IEditingContext.EDITING_CONTEXT, editingContext); - variableManager.put(Environment.ENVIRONMENT, new Environment(Environment.SIRIUS_COMPONENTS)); - variableManager.put(VariableManager.SELF, self.get()); - variableManager.put(Node.SELECTED_NODE, node.orElse(null)); - + VariableManager variableManager = this.populateVariableManager(editingContext, diagramContext, node, edge, self); String selectionDescriptionId = tool.getSelectionDescriptionId(); if (selectionDescriptionId != null && selectedObjectId != null) { var selectionDescriptionOpt = this.representationDescriptionSearchService.findById(editingContext, selectionDescriptionId); @@ -172,4 +165,32 @@ private IStatus executeTool(IEditingContext editingContext, IDiagramContext diag return result; } + private Optional getCurrentContext(IEditingContext editingContext, String diagramElementId, SingleClickOnDiagramElementTool tool, Diagram diagram, Optional node, + Optional edge) { + Optional self = Optional.empty(); + if (node.isPresent()) { + self = this.objectService.getObject(editingContext, node.get().getTargetObjectId()); + } else if (edge.isPresent()) { + self = this.objectService.getObject(editingContext, edge.get().getTargetObjectId()); + } else if (Objects.equals(diagram.getId(), diagramElementId)) { + self = this.objectService.getObject(editingContext, diagram.getTargetObjectId()); + } else { + this.logger.warn("The node creation tool {0} cannot be applied on the current diagram {1} and editing context {2}", tool.getId(), diagram.getId(), editingContext.getId()); + } + return self; + } + + private VariableManager populateVariableManager(IEditingContext editingContext, IDiagramContext diagramContext, Optional node, Optional edge, Optional self) { + VariableManager variableManager = new VariableManager(); + variableManager.put(IDiagramContext.DIAGRAM_CONTEXT, diagramContext); + variableManager.put(IEditingContext.EDITING_CONTEXT, editingContext); + variableManager.put(Environment.ENVIRONMENT, new Environment(Environment.SIRIUS_COMPONENTS)); + variableManager.put(VariableManager.SELF, self.get()); + if (node.isPresent()) { + variableManager.put(Node.SELECTED_NODE, node.get()); + } else if (edge.isPresent()) { + variableManager.put(Edge.SELECTED_EDGE, edge.get()); + } + return variableManager; + } } diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/schema/diagram.graphqls b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/schema/diagram.graphqls index 730e27b9ee..9b5c819c84 100644 --- a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/schema/diagram.graphqls +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/resources/schema/diagram.graphqls @@ -176,7 +176,7 @@ type SingleClickOnDiagramElementTool implements Tool { imageURL: String! appliesToDiagramRoot: Boolean! selectionDescriptionId: String - targetDescriptions: [NodeDescription!]! + targetDescriptions: [DiagramElementDescription!]! } type SingleClickOnTwoDiagramElementsTool implements Tool { @@ -202,14 +202,19 @@ type DiagramDescription implements RepresentationDescription { initialDirectEditElementLabel(labelId: ID!): String! } -type NodeDescription { +interface DiagramElementDescription { + id: ID! + synchronizationPolicy: SynchronizationPolicy! +} + +type NodeDescription implements DiagramElementDescription { id: ID! synchronizationPolicy: SynchronizationPolicy! borderNodeDescriptions: [NodeDescription!]! childNodeDescriptions: [NodeDescription!]! } -type EdgeDescription { +type EdgeDescription implements DiagramElementDescription { id: ID! synchronizationPolicy: SynchronizationPolicy! sourceNodeDescriptions: [NodeDescription!]! diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/test/java/org/eclipse/sirius/components/collaborative/diagrams/handlers/InvokeSingleClickOnDiagramElementToolEventHandlerTests.java b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/test/java/org/eclipse/sirius/components/collaborative/diagrams/handlers/InvokeSingleClickOnDiagramElementToolEventHandlerTests.java index baeddeecee..54e8e7b845 100644 --- a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/test/java/org/eclipse/sirius/components/collaborative/diagrams/handlers/InvokeSingleClickOnDiagramElementToolEventHandlerTests.java +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/test/java/org/eclipse/sirius/components/collaborative/diagrams/handlers/InvokeSingleClickOnDiagramElementToolEventHandlerTests.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; import java.util.UUID; import org.eclipse.sirius.components.collaborative.api.ChangeDescription; @@ -28,11 +29,30 @@ import org.eclipse.sirius.components.collaborative.diagrams.dto.InvokeSingleClickOnDiagramElementToolInput; import org.eclipse.sirius.components.collaborative.diagrams.dto.InvokeSingleClickOnDiagramElementToolSuccessPayload; import org.eclipse.sirius.components.collaborative.diagrams.messages.ICollaborativeDiagramMessageService; +import org.eclipse.sirius.components.core.api.ErrorPayload; import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.core.api.IObjectService; import org.eclipse.sirius.components.core.api.IPayload; import org.eclipse.sirius.components.core.api.IRepresentationDescriptionSearchService; +import org.eclipse.sirius.components.diagrams.ArrowStyle; import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.diagrams.Edge; +import org.eclipse.sirius.components.diagrams.EdgeStyle; +import org.eclipse.sirius.components.diagrams.FreeFormLayoutStrategy; +import org.eclipse.sirius.components.diagrams.Label; +import org.eclipse.sirius.components.diagrams.LabelStyle; +import org.eclipse.sirius.components.diagrams.LineStyle; +import org.eclipse.sirius.components.diagrams.Node; +import org.eclipse.sirius.components.diagrams.NodeType; +import org.eclipse.sirius.components.diagrams.Position; +import org.eclipse.sirius.components.diagrams.Ratio; +import org.eclipse.sirius.components.diagrams.Size; +import org.eclipse.sirius.components.diagrams.ViewModifier; +import org.eclipse.sirius.components.diagrams.description.EdgeDescription; +import org.eclipse.sirius.components.diagrams.description.IDiagramElementDescription; +import org.eclipse.sirius.components.diagrams.description.LabelDescription; +import org.eclipse.sirius.components.diagrams.description.LabelStyleDescription; +import org.eclipse.sirius.components.diagrams.description.NodeDescription; import org.eclipse.sirius.components.diagrams.tests.TestDiagramBuilder; import org.eclipse.sirius.components.diagrams.tools.ITool; import org.eclipse.sirius.components.diagrams.tools.SingleClickOnDiagramElementTool; @@ -50,8 +70,35 @@ * @author sbegaudeau */ public class InvokeSingleClickOnDiagramElementToolEventHandlerTests { + + private static final String DIAGRAM_ID = "diagramId"; + + private static final String EDGE_1_ID = "edge1"; + + private static final String EDGE_DESCRIPTION_ID = "edgeDescriptionId"; + + private static final String EDITING_CONTEXT_ID = "editingContextId"; + + private static final String NODE_DESCRIPTION_ID = "nodeDescriptionId"; + + private static final String NODE_1_ID = "node1"; + + private static final String OBJECT_1_ID = "object1"; + + private static final String LINK_1_ID = "link1"; + + private static final String REPRESENTATION_ID = "representationId"; + + private static final String SELECTED_OBJECT_ID = "selectedObjectId"; + + private static final String TOOL_ID = "toolId"; + + private static final String TOOL_IMAGE_URL = "imageURL"; + + private static final String TOOL_LABEL = "label"; + @Test - public void testInvokeTool() { + public void testInvokeToolOnDiagram() { var objectService = new IObjectService.NoOp() { @Override public Optional getObject(IEditingContext editingContext, String objectId) { @@ -61,16 +108,7 @@ public Optional getObject(IEditingContext editingContext, String objectI var diagramQueryService = new IDiagramQueryService.NoOp(); - // @formatter:off - var tool = SingleClickOnDiagramElementTool.newSingleClickOnDiagramElementTool("toolId") - .label("label") - .imageURL("imageURL") - .targetDescriptions(List.of()) - .selectionDescriptionId(null) - .handler(variableManager -> new Success(ChangeKind.SEMANTIC_CHANGE, Map.of())) - .appliesToDiagramRoot(true) - .build(); - // @formatter:on + var tool = this.createTool(TOOL_ID, true, List.of()); var toolService = new IToolService.NoOp() { @Override @@ -83,16 +121,16 @@ public Optional findToolById(IEditingContext editingContext, Diagram diag var handler = new InvokeSingleClickOnDiagramElementToolEventHandler(objectService, diagramQueryService, toolService, new ICollaborativeDiagramMessageService.NoOp(), new SimpleMeterRegistry(), representationDescriptionSearchService); - var input = new InvokeSingleClickOnDiagramElementToolInput(UUID.randomUUID(), "editingContextId", "representationId", "diagramId", "toolId", 5.0, 8.0, "selectedObjectId"); + var input = new InvokeSingleClickOnDiagramElementToolInput(UUID.randomUUID(), EDITING_CONTEXT_ID, REPRESENTATION_ID, DIAGRAM_ID, TOOL_ID, 5.0, 8.0, SELECTED_OBJECT_ID); - IEditingContext editingContext = () -> "editingContextId"; + IEditingContext editingContext = () -> EDITING_CONTEXT_ID; assertThat(handler.canHandle(input)).isTrue(); Many changeDescriptionSink = Sinks.many().unicast().onBackpressureBuffer(); One payloadSink = Sinks.one(); - IDiagramContext diagramContext = new DiagramContext(new TestDiagramBuilder().getDiagram("diagramId")); + IDiagramContext diagramContext = new DiagramContext(new TestDiagramBuilder().getDiagram(DIAGRAM_ID)); handler.handle(payloadSink, changeDescriptionSink, editingContext, diagramContext, input); ChangeDescription changeDescription = changeDescriptionSink.asFlux().blockFirst(); @@ -101,4 +139,386 @@ public Optional findToolById(IEditingContext editingContext, Diagram diag IPayload payload = payloadSink.asMono().block(); assertThat(payload).isInstanceOf(InvokeSingleClickOnDiagramElementToolSuccessPayload.class); } + + @Test + public void testInvokeToolOnNode() { + var object1 = new Object(); + + var objectService = new IObjectService.NoOp() { + @Override + public Optional getObject(IEditingContext editingContext, String objectId) { + return Optional.of(object1); + } + }; + + var nodeDescriptionId = UUID.nameUUIDFromBytes(NODE_DESCRIPTION_ID.getBytes()); + var nodeDescription = this.createNodeDescription(nodeDescriptionId); + var node1 = this.createNode(NODE_1_ID, nodeDescriptionId, OBJECT_1_ID); + + var diagramQueryService = new IDiagramQueryService.NoOp() { + @Override + public Optional findNodeById(Diagram diagram, String nodeId) { + return Optional.of(node1); + } + }; + + var tool = this.createTool(TOOL_ID, false, List.of(nodeDescription)); + + var toolService = new IToolService.NoOp() { + @Override + public Optional findToolById(IEditingContext editingContext, Diagram diagram, String toolId) { + return Optional.of(tool); + } + }; + var representationDescriptionSearchService = new IRepresentationDescriptionSearchService.NoOp(); + + var handler = new InvokeSingleClickOnDiagramElementToolEventHandler(objectService, diagramQueryService, toolService, new ICollaborativeDiagramMessageService.NoOp(), new SimpleMeterRegistry(), + representationDescriptionSearchService); + + var input = new InvokeSingleClickOnDiagramElementToolInput(UUID.randomUUID(), EDITING_CONTEXT_ID, REPRESENTATION_ID, NODE_1_ID, TOOL_ID, 5.0, 8.0, SELECTED_OBJECT_ID); + + IEditingContext editingContext = () -> EDITING_CONTEXT_ID; + + assertThat(handler.canHandle(input)).isTrue(); + + Many changeDescriptionSink = Sinks.many().unicast().onBackpressureBuffer(); + One payloadSink = Sinks.one(); + + IDiagramContext diagramContext = new DiagramContext(new TestDiagramBuilder().getDiagram(DIAGRAM_ID)); + handler.handle(payloadSink, changeDescriptionSink, editingContext, diagramContext, input); + + ChangeDescription changeDescription = changeDescriptionSink.asFlux().blockFirst(); + assertThat(changeDescription.getKind()).isEqualTo(ChangeKind.SEMANTIC_CHANGE); + + IPayload payload = payloadSink.asMono().block(); + assertThat(payload).isInstanceOf(InvokeSingleClickOnDiagramElementToolSuccessPayload.class); + } + + @Test + public void testInvokeToolOnWrongNode() { + var object1 = new Object(); + + var objectService = new IObjectService.NoOp() { + @Override + public Optional getObject(IEditingContext editingContext, String objectId) { + return Optional.of(object1); + } + }; + + var nodeDescriptionId = UUID.nameUUIDFromBytes(NODE_DESCRIPTION_ID.getBytes()); + var nodeDescription = this.createNodeDescription(nodeDescriptionId); + var node1 = this.createNode(NODE_1_ID, nodeDescriptionId, OBJECT_1_ID); + + var diagramQueryService = new IDiagramQueryService.NoOp() { + @Override + public Optional findNodeById(Diagram diagram, String nodeId) { + if (nodeId.equals(node1.getId())) { + return Optional.of(node1); + } + return Optional.empty(); + } + }; + + var tool = this.createTool(TOOL_ID, false, List.of(nodeDescription)); + + var toolService = new IToolService.NoOp() { + @Override + public Optional findToolById(IEditingContext editingContext, Diagram diagram, String toolId) { + return Optional.of(tool); + } + }; + var representationDescriptionSearchService = new IRepresentationDescriptionSearchService.NoOp(); + + var handler = new InvokeSingleClickOnDiagramElementToolEventHandler(objectService, diagramQueryService, toolService, new ICollaborativeDiagramMessageService.NoOp(), new SimpleMeterRegistry(), + representationDescriptionSearchService); + + var input = new InvokeSingleClickOnDiagramElementToolInput(UUID.randomUUID(), EDITING_CONTEXT_ID, REPRESENTATION_ID, "anotherNodeId", TOOL_ID, 5.0, 8.0, SELECTED_OBJECT_ID); + + IEditingContext editingContext = () -> EDITING_CONTEXT_ID; + + assertThat(handler.canHandle(input)).isTrue(); + + Many changeDescriptionSink = Sinks.many().unicast().onBackpressureBuffer(); + One payloadSink = Sinks.one(); + + IDiagramContext diagramContext = new DiagramContext(new TestDiagramBuilder().getDiagram(DIAGRAM_ID)); + handler.handle(payloadSink, changeDescriptionSink, editingContext, diagramContext, input); + + ChangeDescription changeDescription = changeDescriptionSink.asFlux().blockFirst(); + assertThat(changeDescription.getKind()).isEqualTo(ChangeKind.NOTHING); + + IPayload payload = payloadSink.asMono().block(); + assertThat(payload).isInstanceOf(ErrorPayload.class); + } + + @Test + public void testInvokeToolOnEdge() { + var link1 = new Object(); + + var objectService = new IObjectService.NoOp() { + @Override + public Optional getObject(IEditingContext editingContext, String objectId) { + return Optional.of(link1); + } + }; + + var edgeDescriptionId = UUID.nameUUIDFromBytes(EDGE_DESCRIPTION_ID.getBytes()); + var edgeDescription = this.createEdgeDescription(edgeDescriptionId); + var edge1 = this.createEdge(EDGE_1_ID, edgeDescriptionId, LINK_1_ID); + + var diagramQueryService = new IDiagramQueryService.NoOp() { + @Override + public Optional findEdgeById(Diagram diagram, String edgeId) { + if (edgeId.equals(edge1.getId())) { + return Optional.of(edge1); + } + return Optional.empty(); + } + }; + + var tool = this.createTool(TOOL_ID, false, List.of(edgeDescription)); + + var toolService = new IToolService.NoOp() { + @Override + public Optional findToolById(IEditingContext editingContext, Diagram diagram, String toolId) { + return Optional.of(tool); + } + }; + var representationDescriptionSearchService = new IRepresentationDescriptionSearchService.NoOp(); + + var handler = new InvokeSingleClickOnDiagramElementToolEventHandler(objectService, diagramQueryService, toolService, new ICollaborativeDiagramMessageService.NoOp(), new SimpleMeterRegistry(), + representationDescriptionSearchService); + + var input = new InvokeSingleClickOnDiagramElementToolInput(UUID.randomUUID(), EDITING_CONTEXT_ID, REPRESENTATION_ID, EDGE_1_ID, TOOL_ID, 5.0, 8.0, SELECTED_OBJECT_ID); + + IEditingContext editingContext = () -> EDITING_CONTEXT_ID; + + assertThat(handler.canHandle(input)).isTrue(); + + Many changeDescriptionSink = Sinks.many().unicast().onBackpressureBuffer(); + One payloadSink = Sinks.one(); + + IDiagramContext diagramContext = new DiagramContext(new TestDiagramBuilder().getDiagram(DIAGRAM_ID)); + handler.handle(payloadSink, changeDescriptionSink, editingContext, diagramContext, input); + + ChangeDescription changeDescription = changeDescriptionSink.asFlux().blockFirst(); + assertThat(changeDescription.getKind()).isEqualTo(ChangeKind.SEMANTIC_CHANGE); + + IPayload payload = payloadSink.asMono().block(); + assertThat(payload).isInstanceOf(InvokeSingleClickOnDiagramElementToolSuccessPayload.class); + } + + @Test + public void testInvokeToolOnWrongEdge() { + var link1 = new Object(); + + var objectService = new IObjectService.NoOp() { + @Override + public Optional getObject(IEditingContext editingContext, String objectId) { + return Optional.of(link1); + } + }; + + var edgeDescriptionId = UUID.nameUUIDFromBytes(EDGE_DESCRIPTION_ID.getBytes()); + var edgeDescription = this.createEdgeDescription(edgeDescriptionId); + var edge1 = this.createEdge(EDGE_1_ID, edgeDescriptionId, LINK_1_ID); + + var diagramQueryService = new IDiagramQueryService.NoOp() { + @Override + public Optional findEdgeById(Diagram diagram, String edgeId) { + if (edgeId.equals(edge1.getId())) { + return Optional.of(edge1); + } + return Optional.empty(); + } + }; + + var tool = this.createTool(TOOL_ID, false, List.of(edgeDescription)); + + var toolService = new IToolService.NoOp() { + @Override + public Optional findToolById(IEditingContext editingContext, Diagram diagram, String toolId) { + return Optional.of(tool); + } + }; + var representationDescriptionSearchService = new IRepresentationDescriptionSearchService.NoOp(); + + var handler = new InvokeSingleClickOnDiagramElementToolEventHandler(objectService, diagramQueryService, toolService, new ICollaborativeDiagramMessageService.NoOp(), new SimpleMeterRegistry(), + representationDescriptionSearchService); + + var input = new InvokeSingleClickOnDiagramElementToolInput(UUID.randomUUID(), EDITING_CONTEXT_ID, REPRESENTATION_ID, "anotherEdgeId", TOOL_ID, 5.0, 8.0, SELECTED_OBJECT_ID); + + IEditingContext editingContext = () -> EDITING_CONTEXT_ID; + + assertThat(handler.canHandle(input)).isTrue(); + + Many changeDescriptionSink = Sinks.many().unicast().onBackpressureBuffer(); + One payloadSink = Sinks.one(); + + IDiagramContext diagramContext = new DiagramContext(new TestDiagramBuilder().getDiagram(DIAGRAM_ID)); + handler.handle(payloadSink, changeDescriptionSink, editingContext, diagramContext, input); + + ChangeDescription changeDescription = changeDescriptionSink.asFlux().blockFirst(); + assertThat(changeDescription.getKind()).isEqualTo(ChangeKind.NOTHING); + + IPayload payload = payloadSink.asMono().block(); + assertThat(payload).isInstanceOf(ErrorPayload.class); + } + + private SingleClickOnDiagramElementTool createTool(String toolId, boolean appliesToDiagramRoot, List diagramElementsDescriptions) { + // @formatter:off + return SingleClickOnDiagramElementTool.newSingleClickOnDiagramElementTool(toolId) + .label(TOOL_LABEL) + .imageURL(TOOL_IMAGE_URL) + .targetDescriptions(diagramElementsDescriptions) + .selectionDescriptionId(null) + .handler(variableManager -> new Success(ChangeKind.SEMANTIC_CHANGE, Map.of())) + .appliesToDiagramRoot(appliesToDiagramRoot) + .build(); + // @formatter:on + } + + private Node createNode(String nodeId, UUID nodeDescriptionId, String targetObjectId) { + // @formatter:off + var labelStyle = LabelStyle.newLabelStyle() + .color("#000001") + .fontSize(16) + .iconURL("") + .build(); + var label = Label.newLabel(UUID.randomUUID().toString()) + .type("labelType") + .text("text") + .position(Position.UNDEFINED) + .size(Size.UNDEFINED) + .alignment(Position.UNDEFINED) + .style(labelStyle) + .build(); + + return Node.newNode(nodeId) + .type(NodeType.NODE_RECTANGLE) + .targetObjectId(targetObjectId) + .targetObjectKind("") + .targetObjectLabel("") + .descriptionId(nodeDescriptionId) + .label(label) + .style(new TestDiagramBuilder().getRectangularNodeStyle()) + .childrenLayoutStrategy(new FreeFormLayoutStrategy()) + .position(Position.UNDEFINED) + .size(Size.UNDEFINED) + .borderNodes(List.of()) + .childNodes(List.of()) + .modifiers(Set.of()) + .state(ViewModifier.Normal) + .build(); + // @formatter:on + } + + private NodeDescription createNodeDescription(UUID nodeDescriptionId) { + // @formatter:off + var styleDescription = LabelStyleDescription.newLabelStyleDescription() + .colorProvider(variableManager -> "") + .fontSizeProvider(variableManager -> 0) + .boldProvider(variableManager -> false) + .italicProvider(variableManager -> false) + .underlineProvider(variableManager -> false) + .strikeThroughProvider(variableManager -> false) + .iconURLProvider(variableManager -> "") + .build(); + + var labelDescription = LabelDescription.newLabelDescription("labelDescription") + .idProvider(vm -> "") + .styleDescriptionProvider(vm -> styleDescription) + .textProvider(vm -> "") + .build(); + + return NodeDescription.newNodeDescription(nodeDescriptionId) + .borderNodeDescriptions(List.of()) + .childNodeDescriptions(List.of()) + .childrenLayoutStrategyProvider(vm -> new FreeFormLayoutStrategy()) + .deleteHandler(vm -> new Success()) + .labelDescription(labelDescription) + .labelEditHandler((vm, newLabel) -> new Success()) + .reusedBorderNodeDescriptionIds(List.of()) + .reusedChildNodeDescriptionIds(List.of()) + .semanticElementsProvider(vm -> List.of()) + .sizeProvider(vm -> Size.UNDEFINED) + .styleProvider(vm -> null) + .targetObjectIdProvider(vm -> "") + .targetObjectKindProvider(vm -> "") + .targetObjectLabelProvider(vm -> "") + .typeProvider(vm -> "") + .build(); + // @formatter:on + } + + private Edge createEdge(String edgeId, UUID edgeDescriptionId, String targetObjectId) { + // @formatter:off + var labelStyle = LabelStyle.newLabelStyle() + .color("#000002") + .fontSize(14) + .iconURL("") + .build(); + + var label = Label.newLabel(UUID.randomUUID().toString()) + .alignment(Position.UNDEFINED) + .position(Position.UNDEFINED) + .size(Size.UNDEFINED) + .style(labelStyle) + .text("text") + .type("labelType") + .build(); + + var edgeStyle = EdgeStyle.newEdgeStyle() + .color("#000000") + .lineStyle(LineStyle.Solid) + .size(1) + .sourceArrow(ArrowStyle.None) + .targetArrow(ArrowStyle.InputArrow) + .build(); + + return Edge.newEdge(edgeId) + .type("edge:straight") + .sourceId("") + .targetId("") + .sourceAnchorRelativePosition(Ratio.UNDEFINED) + .targetAnchorRelativePosition(Ratio.UNDEFINED) + .beginLabel(label) + .centerLabel(label) + .endLabel(label) + .descriptionId(edgeDescriptionId) + .routingPoints(List.of()) + .style(edgeStyle) + .targetObjectId(targetObjectId) + .targetObjectKind("") + .targetObjectLabel("") + .modifiers(Set.of()) + .state(ViewModifier.Normal) + .build(); + // @formatter:on + } + + private EdgeDescription createEdgeDescription(UUID edgeDescriptionId) { + // @formatter:off + var edgeStyle = EdgeStyle.newEdgeStyle() + .color("#000003") + .lineStyle(LineStyle.Dash) + .sourceArrow(ArrowStyle.Diamond) + .targetArrow(ArrowStyle.Diamond) + .build(); + + return EdgeDescription.newEdgeDescription(edgeDescriptionId) + .deleteHandler(vm -> new Success()) + .labelEditHandler((vm, newLabel) -> new Success()) + .semanticElementsProvider(vm -> List.of(new Object())) + .sourceNodeDescriptions(List.of()) + .sourceNodesProvider(vm -> List.of()) + .styleProvider(vm -> edgeStyle) + .targetNodeDescriptions(List.of()) + .targetNodesProvider(vm -> List.of()) + .targetObjectIdProvider(vm -> "") + .targetObjectKindProvider(vm -> "") + .targetObjectLabelProvider(vm -> "") + .build(); + // @formatter:on + } } diff --git a/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/description/EdgeDescription.java b/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/description/EdgeDescription.java index ae987deb7c..5002cded95 100644 --- a/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/description/EdgeDescription.java +++ b/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/description/EdgeDescription.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2022 Obeo. + * Copyright (c) 2019, 2023 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -31,7 +31,7 @@ * @author sbegaudeau */ @Immutable -public final class EdgeDescription { +public final class EdgeDescription implements IDiagramElementDescription { /** * The name of the variables which points to the representation element at the source/origin of a particular edge. */ @@ -89,6 +89,7 @@ private EdgeDescription() { // Prevent instantiation } + @Override public UUID getId() { return this.id; } diff --git a/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/description/IDiagramElementDescription.java b/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/description/IDiagramElementDescription.java new file mode 100644 index 0000000000..3e6e26a619 --- /dev/null +++ b/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/description/IDiagramElementDescription.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2023 Obeo. + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.components.diagrams.description; + +import java.util.UUID; + +/** + * The description of a diagram element. + * + * @author arichard + */ +public interface IDiagramElementDescription { + + UUID getId(); + +} diff --git a/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/description/NodeDescription.java b/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/description/NodeDescription.java index b12b0120e6..82ef9865c2 100644 --- a/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/description/NodeDescription.java +++ b/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/description/NodeDescription.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2022 Obeo. + * Copyright (c) 2019, 2023 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -33,7 +33,7 @@ * @author sbegaudeau */ @Immutable -public final class NodeDescription { +public final class NodeDescription implements IDiagramElementDescription { private UUID id; private SynchronizationPolicy synchronizationPolicy; @@ -72,6 +72,7 @@ private NodeDescription() { // Prevent instantiation } + @Override public UUID getId() { return this.id; } diff --git a/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/tools/SingleClickOnDiagramElementTool.java b/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/tools/SingleClickOnDiagramElementTool.java index 358712c422..1061ffd7a0 100644 --- a/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/tools/SingleClickOnDiagramElementTool.java +++ b/packages/diagrams/backend/sirius-components-diagrams/src/main/java/org/eclipse/sirius/components/diagrams/tools/SingleClickOnDiagramElementTool.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2019, 2022 Obeo. + * Copyright (c) 2019, 2023 Obeo. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -18,7 +18,7 @@ import java.util.function.Function; import org.eclipse.sirius.components.annotations.Immutable; -import org.eclipse.sirius.components.diagrams.description.NodeDescription; +import org.eclipse.sirius.components.diagrams.description.IDiagramElementDescription; import org.eclipse.sirius.components.representations.IStatus; import org.eclipse.sirius.components.representations.VariableManager; @@ -41,7 +41,7 @@ public final class SingleClickOnDiagramElementTool implements ITool { private Function handler; - private List targetDescriptions; + private List targetDescriptions; private boolean appliesToDiagramRoot; @@ -51,7 +51,7 @@ private SingleClickOnDiagramElementTool() { // Prevent instantiation } - public List getTargetDescriptions() { + public List getTargetDescriptions() { return this.targetDescriptions; } @@ -109,7 +109,7 @@ public static final class Builder { private Function handler; - private List targetDescriptions; + private List targetDescriptions; private boolean appliesToDiagramRoot; @@ -129,7 +129,7 @@ public Builder label(String label) { return this; } - public Builder targetDescriptions(List targetDescriptions) { + public Builder targetDescriptions(List targetDescriptions) { this.targetDescriptions = Objects.requireNonNull(targetDescriptions); return this; } diff --git a/packages/diagrams/frontend/sirius-components-diagrams/src/representation/DiagramRepresentation.types.ts b/packages/diagrams/frontend/sirius-components-diagrams/src/representation/DiagramRepresentation.types.ts index 30c9ed1231..9b5f59106a 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams/src/representation/DiagramRepresentation.types.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams/src/representation/DiagramRepresentation.types.ts @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021, 2022 Obeo and others. + * Copyright (c) 2021, 2023 Obeo and others. * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v2.0 * which accompanies this distribution, and is available at @@ -88,7 +88,7 @@ export interface Tool { export interface SingleClickOnDiagramElementTool extends Tool { appliesToDiagramRoot: boolean; selectionDescriptionId: string; - targetDescriptions: NodeDescription[]; + targetDescriptions: DiagramElementDescription[]; } export interface SingleClickOnTwoDiagramElementsTool extends Tool { @@ -100,7 +100,11 @@ export interface SingleClickOnTwoDiagramElementsCandidate { targets: NodeDescription[]; } -export interface NodeDescription { +export interface DiagramElementDescription { + id: string; +} + +export interface NodeDescription extends DiagramElementDescription { id: string; } diff --git a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/ViewDiagramDescriptionConverter.java b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/ViewDiagramDescriptionConverter.java index 759bf743d4..9b2b8f060a 100644 --- a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/ViewDiagramDescriptionConverter.java +++ b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/ViewDiagramDescriptionConverter.java @@ -322,7 +322,7 @@ private List createToolSections(ViewDiagramDescriptionConverterCont private List getNodeTools(ViewDiagramDescriptionConverterContext converterContext, Map capturedConvertedNodes) { List nodeCreationTools = new ArrayList<>(); for (var nodeDescription : converterContext.getConvertedNodes().keySet()) { - List allTargetDescriptions = this.getAllTargetDescriptions(nodeDescription, converterContext); + List allTargetDescriptions = this.getAllTargetDescriptions(nodeDescription, converterContext); String imageURL = this.viewToolImageProvider.getImage(nodeDescription); // Add custom tools @@ -412,8 +412,9 @@ private List getEdgeTools(ViewDiagramDescriptionConverterContext converte return edgeTools; } - private List getAllTargetDescriptions(org.eclipse.sirius.components.view.NodeDescription nodeDescription, ViewDiagramDescriptionConverterContext converterContext) { - List allTargetDescriptions = new ArrayList<>(); + private List getAllTargetDescriptions(org.eclipse.sirius.components.view.NodeDescription nodeDescription, + ViewDiagramDescriptionConverterContext converterContext) { + List allTargetDescriptions = new ArrayList<>(); var targetDescriptions = Optional.ofNullable(nodeDescription.eContainer()).map(converterContext.getConvertedNodes()::get).stream().toList(); allTargetDescriptions.addAll(targetDescriptions); diff --git a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/ViewToolSectionsProvider.java b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/ViewToolSectionsProvider.java index 51605e86c7..74b2c6f97e 100644 --- a/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/ViewToolSectionsProvider.java +++ b/packages/view/backend/sirius-components-view-emf/src/main/java/org/eclipse/sirius/components/view/emf/diagram/ViewToolSectionsProvider.java @@ -21,9 +21,11 @@ import org.eclipse.sirius.components.collaborative.diagrams.api.IToolSectionsProvider; import org.eclipse.sirius.components.compatibility.api.IIdentifierProvider; import org.eclipse.sirius.components.diagrams.Diagram; +import org.eclipse.sirius.components.diagrams.Edge; import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.sirius.components.diagrams.description.DiagramDescription; import org.eclipse.sirius.components.diagrams.description.EdgeDescription; +import org.eclipse.sirius.components.diagrams.description.IDiagramElementDescription; import org.eclipse.sirius.components.diagrams.description.NodeDescription; import org.eclipse.sirius.components.diagrams.description.SynchronizationPolicy; import org.eclipse.sirius.components.diagrams.tools.ITool; @@ -61,10 +63,12 @@ public List handle(Object targetElement, Object diagramElement, Obj if (diagramElement instanceof Diagram) { toolSections.addAll(this.getDiagramToolSections(diagramDescription)); } - if (diagramElement instanceof Node && diagramElementDescription instanceof NodeDescription) { - NodeDescription nodeDescription = (NodeDescription) diagramElementDescription; + if (diagramElement instanceof Node && diagramElementDescription instanceof NodeDescription nodeDescription) { toolSections.addAll(this.getNodeToolSections(diagramDescription, nodeDescription)); } + if (diagramElement instanceof Edge && diagramElementDescription instanceof EdgeDescription edgeDescription) { + toolSections.addAll(this.getEdgeToolSections(diagramDescription, edgeDescription)); + } toolSections.addAll(this.createExtraToolSections(diagramElementDescription)); return toolSections; @@ -101,17 +105,30 @@ private List getNodeToolSections(DiagramDescription diagramDescript return toolSections; } - private boolean isValidTool(ITool tool, NodeDescription nodeDescription) { - boolean isValidTool = tool instanceof SingleClickOnDiagramElementTool && ((SingleClickOnDiagramElementTool) tool).getTargetDescriptions().contains(nodeDescription); + private List getEdgeToolSections(DiagramDescription diagramDescription, EdgeDescription edgeDescription) { + List toolSections = new ArrayList<>(); + for (ToolSection toolSection : diagramDescription.getToolSections()) { + List tools = toolSection.getTools().stream().filter(tool -> this.isValidTool(tool, edgeDescription)).toList(); + + if (!tools.isEmpty()) { + ToolSection filteredToolSection = ToolSection.newToolSection(toolSection).tools(tools).build(); + toolSections.add(filteredToolSection); + } + } + return toolSections; + } + + private boolean isValidTool(ITool tool, IDiagramElementDescription diagramElementDescription) { + boolean isValidTool = tool instanceof SingleClickOnDiagramElementTool && ((SingleClickOnDiagramElementTool) tool).getTargetDescriptions().contains(diagramElementDescription); isValidTool = isValidTool || tool instanceof SingleClickOnTwoDiagramElementsTool - && ((SingleClickOnTwoDiagramElementsTool) tool).getCandidates().stream().anyMatch(edgeCandidate -> edgeCandidate.getSources().contains(nodeDescription)); + && ((SingleClickOnTwoDiagramElementsTool) tool).getCandidates().stream().anyMatch(edgeCandidate -> edgeCandidate.getSources().contains(diagramElementDescription)); return isValidTool; } private List createExtraToolSections(Object diagramElementDescription) { List extraToolSections = new ArrayList<>(); - List targetDescriptions = new ArrayList<>(); + List targetDescriptions = new ArrayList<>(); boolean unsynchronizedMapping = false; //@formatter:off if (diagramElementDescription instanceof NodeDescription) {