diff --git a/doc/how-to/contribute-custom-tool.adoc b/doc/how-to/contribute-custom-tool.adoc index 5a432ad665..a35e0182a5 100644 --- a/doc/how-to/contribute-custom-tool.adoc +++ b/doc/how-to/contribute-custom-tool.adoc @@ -27,3 +27,26 @@ The `DiagramPaletteToolContribution` need two props, the _component_ and a _canH It must return true only for the palette where the custom tool needs to be added. To compute that, this function has two parameters the _diagramId_ and the _diagramElementId_, note that for the diagram palette _diagramId_ = _diagramElementId_. To retrieve the node metadata, you can use the hook `useNodes` and filter all the node by the _diagramElementId_. + +== Specify a reference position + +Depending on the tool purpose, the click coordinates may be necessary for the result action. +For example, when the tool creates a new element on the diagram. +To achieve this, we've added a new API `IDiagramInputReferencePositionProvider`. +Where it's possible to define the way the reference position must be retrieved depending on the input. + +[source,java] +---- +public interface IDiagramInputReferencePositionProvider { + + boolean canHandle(IInput diagramInput); + + ReferencePosition getReferencePosition(IInput diagramInput, IDiagramContext diagramContext); +} +---- + +For the generic tool used in sirius-component, the provider is specified in the service `GenericDiagramToolReferencePositionProvider`. + +The basic case is to use the position of the pallet as a reference position. +These coordinates are available in the props `DiagramPaletteToolContributionComponentProps`. + diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessor.java b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessor.java index d45adc0bb0..8bf756a27d 100644 --- a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessor.java +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessor.java @@ -29,10 +29,8 @@ import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramEventHandler; import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramEventProcessor; import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramInput; +import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramInputReferencePositionProvider; import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramRefreshedEventPayload; -import org.eclipse.sirius.components.collaborative.diagrams.dto.DropNodeInput; -import org.eclipse.sirius.components.collaborative.diagrams.dto.DropOnDiagramInput; -import org.eclipse.sirius.components.collaborative.diagrams.dto.InvokeSingleClickOnDiagramElementToolInput; import org.eclipse.sirius.components.collaborative.diagrams.dto.LayoutDiagramInput; import org.eclipse.sirius.components.collaborative.diagrams.dto.NodeLayoutDataInput; import org.eclipse.sirius.components.collaborative.diagrams.dto.ReferencePosition; @@ -48,7 +46,6 @@ import org.eclipse.sirius.components.diagrams.description.DiagramDescription; import org.eclipse.sirius.components.diagrams.layoutdata.DiagramLayoutData; import org.eclipse.sirius.components.diagrams.layoutdata.NodeLayoutData; -import org.eclipse.sirius.components.diagrams.layoutdata.Position; import org.eclipse.sirius.components.representations.IRepresentation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -86,6 +83,8 @@ public class DiagramEventProcessor implements IDiagramEventProcessor { private final DiagramEventFlux diagramEventFlux; + private final List diagramInputReferencePositionProviders; + private UUID currentRevisionId = UUID.randomUUID(); private String currentRevisionCause = DiagramRefreshedEventPayload.CAUSE_REFRESH; @@ -101,12 +100,13 @@ public DiagramEventProcessor(DiagramEventProcessorParameters parameters) { this.representationRefreshPolicyRegistry = parameters.representationRefreshPolicyRegistry(); this.representationPersistenceService = parameters.representationPersistenceService(); this.diagramCreationService = parameters.diagramCreationService(); + this.diagramInputReferencePositionProviders = parameters.diagramInputReferencePositionProviders(); // We automatically refresh the representation before using it since things may have changed since the moment it // has been saved in the database. This is quite similar to the auto-refresh on loading in Sirius. - Diagram diagram = this.diagramCreationService.refresh(editingContext, diagramContext).orElse(null); + Diagram diagram = this.diagramCreationService.refresh(this.editingContext, this.diagramContext).orElse(null); this.representationPersistenceService.save(parameters.editingContext(), diagram); - diagramContext.update(diagram); + this.diagramContext.update(diagram); this.diagramEventFlux = new DiagramEventFlux(diagram); if (diagram != null) { @@ -194,19 +194,12 @@ public void refresh(ChangeDescription changeDescription) { } private ReferencePosition getReferencePosition(IInput diagramInput) { - ReferencePosition referencePosition = null; - if (diagramInput instanceof InvokeSingleClickOnDiagramElementToolInput input) { - String parentId = null; - if (!this.diagramContext.getDiagram().getId().equals(input.diagramElementId())) { - parentId = input.diagramElementId(); - } - referencePosition = new ReferencePosition(parentId, new Position(input.startingPositionX(), input.startingPositionY())); - } else if (diagramInput instanceof DropNodeInput input) { - referencePosition = new ReferencePosition(input.targetElementId(), new Position(input.x(), input.y())); - } else if (diagramInput instanceof DropOnDiagramInput input) { - referencePosition = new ReferencePosition(null, new Position(input.startingPositionX(), input.startingPositionY())); - } - return referencePosition; + + return this.diagramInputReferencePositionProviders.stream() + .filter(handler -> handler.canHandle(diagramInput)) + .findFirst() + .map(provider -> provider.getReferencePosition(diagramInput, this.diagramContext)) + .orElse(null); } /** @@ -214,7 +207,7 @@ private ReferencePosition getReferencePosition(IInput diagramInput) { * diagram (not other diagrams). * * @param changeDescription - * The change description + * The change description * @return true if the diagram should be refreshed, false otherwise */ public boolean shouldRefresh(ChangeDescription changeDescription) { diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessorFactory.java b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessorFactory.java index d2fd681a13..21abf46288 100644 --- a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessorFactory.java +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessorFactory.java @@ -28,6 +28,7 @@ import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramCreationService; import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramEventHandler; import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramEventProcessor; +import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramInputReferencePositionProvider; import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.core.api.IRepresentationDescriptionSearchService; import org.eclipse.sirius.components.diagrams.Diagram; @@ -55,8 +56,10 @@ public class DiagramEventProcessorFactory implements IRepresentationEventProcess private final IRepresentationPersistenceService representationPersistenceService; + private final List diagramInputReferencePositionProviders; + public DiagramEventProcessorFactory(RepresentationEventProcessorFactoryConfiguration configuration, IDiagramCreationService diagramCreationService, - List diagramEventHandlers, IRepresentationPersistenceService representationPersistenceService) { + List diagramEventHandlers, IRepresentationPersistenceService representationPersistenceService, List diagramInputReferencePositionProviders) { this.representationSearchService = Objects.requireNonNull(configuration.getRepresentationSearchService()); this.diagramCreationService = Objects.requireNonNull(diagramCreationService); this.diagramEventHandlers = Objects.requireNonNull(diagramEventHandlers); @@ -64,6 +67,7 @@ public DiagramEventProcessorFactory(RepresentationEventProcessorFactoryConfigura this.subscriptionManagerFactory = Objects.requireNonNull(configuration.getSubscriptionManagerFactory()); this.representationDescriptionSearchService = Objects.requireNonNull(configuration.getRepresentationDescriptionSearchService()); this.representationRefreshPolicyRegistry = Objects.requireNonNull(configuration.getRepresentationRefreshPolicyRegistry()); + this.diagramInputReferencePositionProviders = Objects.requireNonNull(diagramInputReferencePositionProviders); } @Override @@ -89,6 +93,7 @@ public Optional createRepresentatio .representationDescriptionSearchService(this.representationDescriptionSearchService) .representationRefreshPolicyRegistry(this.representationRefreshPolicyRegistry) .representationPersistenceService(this.representationPersistenceService) + .diagramInputReferencePositionProviders(this.diagramInputReferencePositionProviders) .build(); IRepresentationEventProcessor diagramEventProcessor = new DiagramEventProcessor(parameters); diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessorParameters.java b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessorParameters.java index 4ec3d7d9a5..ef0d6c6bc6 100644 --- a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessorParameters.java +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessorParameters.java @@ -21,6 +21,7 @@ import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramContext; import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramCreationService; import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramEventHandler; +import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramInputReferencePositionProvider; import org.eclipse.sirius.components.core.api.IEditingContext; import org.eclipse.sirius.components.core.api.IRepresentationDescriptionSearchService; @@ -37,8 +38,10 @@ public record DiagramEventProcessorParameters( IDiagramCreationService diagramCreationService, IRepresentationDescriptionSearchService representationDescriptionSearchService, IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry, - IRepresentationPersistenceService representationPersistenceService + IRepresentationPersistenceService representationPersistenceService, + List diagramInputReferencePositionProviders ) { + public DiagramEventProcessorParameters { Objects.requireNonNull(editingContext); Objects.requireNonNull(diagramContext); @@ -48,6 +51,7 @@ public record DiagramEventProcessorParameters( Objects.requireNonNull(representationDescriptionSearchService); Objects.requireNonNull(representationRefreshPolicyRegistry); Objects.requireNonNull(representationPersistenceService); + Objects.requireNonNull(diagramInputReferencePositionProviders); } public static Builder newDiagramEventProcessorParameters() { @@ -62,6 +66,7 @@ public static Builder newDiagramEventProcessorParameters() { @SuppressWarnings("checkstyle:HiddenField") @org.eclipse.sirius.components.annotations.Builder public static final class Builder { + private IEditingContext editingContext; private IDiagramContext diagramContext; @@ -78,6 +83,8 @@ public static final class Builder { private IRepresentationPersistenceService representationPersistenceService; + private List diagramInputReferencePositionProviders; + private Builder() { // Prevent instantiation } @@ -122,6 +129,11 @@ public Builder representationPersistenceService(IRepresentationPersistenceServic return this; } + public Builder diagramInputReferencePositionProviders(List diagramInputReferencePositionProviders) { + this.diagramInputReferencePositionProviders = Objects.requireNonNull(diagramInputReferencePositionProviders); + return this; + } + public DiagramEventProcessorParameters build() { return new DiagramEventProcessorParameters( this.editingContext, @@ -131,7 +143,8 @@ public DiagramEventProcessorParameters build() { this.diagramCreationService, this.representationDescriptionSearchService, this.representationRefreshPolicyRegistry, - this.representationPersistenceService + this.representationPersistenceService, + this.diagramInputReferencePositionProviders ); } } diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/api/IDiagramInputReferencePositionProvider.java b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/api/IDiagramInputReferencePositionProvider.java new file mode 100644 index 0000000000..ba650ffe56 --- /dev/null +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/api/IDiagramInputReferencePositionProvider.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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.collaborative.diagrams.api; + +import org.eclipse.sirius.components.collaborative.diagrams.dto.ReferencePosition; +import org.eclipse.sirius.components.core.api.IInput; + +/** + * Provides diagram input reference position for a diagram. + * + * @author frouene + */ +public interface IDiagramInputReferencePositionProvider { + + boolean canHandle(IInput diagramInput); + + ReferencePosition getReferencePosition(IInput diagramInput, IDiagramContext diagramContext); +} diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/providers/GenericDiagramToolReferencePositionProvider.java b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/providers/GenericDiagramToolReferencePositionProvider.java new file mode 100644 index 0000000000..a2121abb46 --- /dev/null +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/main/java/org/eclipse/sirius/components/collaborative/diagrams/providers/GenericDiagramToolReferencePositionProvider.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * 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.collaborative.diagrams.providers; + +import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramContext; +import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramInputReferencePositionProvider; +import org.eclipse.sirius.components.collaborative.diagrams.dto.DropNodeInput; +import org.eclipse.sirius.components.collaborative.diagrams.dto.DropOnDiagramInput; +import org.eclipse.sirius.components.collaborative.diagrams.dto.InvokeSingleClickOnDiagramElementToolInput; +import org.eclipse.sirius.components.collaborative.diagrams.dto.ReferencePosition; +import org.eclipse.sirius.components.core.api.IInput; +import org.eclipse.sirius.components.diagrams.layoutdata.Position; +import org.springframework.stereotype.Service; + +/** + * Provider use to retrieve the reference position for InvokeSingleClickOnDiagramElementToolInput, DropNodeInput and DropOnDiagramInput input. + * + * @author frouene + */ +@Service +public class GenericDiagramToolReferencePositionProvider implements IDiagramInputReferencePositionProvider { + + @Override + public boolean canHandle(IInput diagramInput) { + return diagramInput instanceof InvokeSingleClickOnDiagramElementToolInput || diagramInput instanceof DropNodeInput || diagramInput instanceof DropOnDiagramInput; + } + + @Override + public ReferencePosition getReferencePosition(IInput diagramInput, IDiagramContext diagramContext) { + ReferencePosition referencePosition = null; + if (diagramInput instanceof InvokeSingleClickOnDiagramElementToolInput input) { + String parentId = null; + if (!diagramContext.getDiagram().getId().equals(input.diagramElementId())) { + parentId = input.diagramElementId(); + } + referencePosition = new ReferencePosition(parentId, new Position(input.startingPositionX(), input.startingPositionY())); + } else if (diagramInput instanceof DropNodeInput input) { + referencePosition = new ReferencePosition(input.targetElementId(), new Position(input.x(), input.y())); + } else if (diagramInput instanceof DropOnDiagramInput input) { + referencePosition = new ReferencePosition(null, new Position(input.startingPositionX(), input.startingPositionY())); + } + return referencePosition; + } +} diff --git a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/test/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessorTests.java b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/test/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessorTests.java index d2e2575e20..1eb3e6b856 100644 --- a/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/test/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessorTests.java +++ b/packages/diagrams/backend/sirius-components-collaborative-diagrams/src/test/java/org/eclipse/sirius/components/collaborative/diagrams/DiagramEventProcessorTests.java @@ -89,6 +89,7 @@ public void testEmitDiagramOnSubscription() { .representationDescriptionSearchService(new IRepresentationDescriptionSearchService.NoOp()) .representationRefreshPolicyRegistry(new IRepresentationRefreshPolicyRegistry.NoOp()) .representationPersistenceService(new IRepresentationPersistenceService.NoOp()) + .diagramInputReferencePositionProviders(List.of()) .build(); DiagramEventProcessor diagramEventProcessor = new DiagramEventProcessor(parameters); @@ -113,6 +114,7 @@ public void testEmitDiagramOnRefresh() { .representationDescriptionSearchService(new IRepresentationDescriptionSearchService.NoOp()) .representationRefreshPolicyRegistry(new IRepresentationRefreshPolicyRegistry.NoOp()) .representationPersistenceService(new IRepresentationPersistenceService.NoOp()) + .diagramInputReferencePositionProviders(List.of()) .build(); DiagramEventProcessor diagramEventProcessor = new DiagramEventProcessor(parameters); @@ -141,6 +143,7 @@ public void testUpdateInitialDiagramForNewSubscription() { .representationDescriptionSearchService(new IRepresentationDescriptionSearchService.NoOp()) .representationRefreshPolicyRegistry(new IRepresentationRefreshPolicyRegistry.NoOp()) .representationPersistenceService(new IRepresentationPersistenceService.NoOp()) + .diagramInputReferencePositionProviders(List.of()) .build(); DiagramEventProcessor diagramEventProcessor = new DiagramEventProcessor(parameters); @@ -176,6 +179,7 @@ public void testCompleteOnDispose() { .representationDescriptionSearchService(new IRepresentationDescriptionSearchService.NoOp()) .representationRefreshPolicyRegistry(new IRepresentationRefreshPolicyRegistry.NoOp()) .representationPersistenceService(new IRepresentationPersistenceService.NoOp()) + .diagramInputReferencePositionProviders(List.of()) .build(); DiagramEventProcessor diagramEventProcessor = new DiagramEventProcessor(parameters); diff --git a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/palette/DiagramPaletteToolContribution.types.ts b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/palette/DiagramPaletteToolContribution.types.ts index 18c2836e78..4121d46747 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/palette/DiagramPaletteToolContribution.types.ts +++ b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/palette/DiagramPaletteToolContribution.types.ts @@ -17,6 +17,8 @@ export interface DiagramPaletteToolContributionProps { } export interface DiagramPaletteToolContributionComponentProps { + x: number; + y: number; diagramElementId: string; key: string; } diff --git a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/palette/Palette.tsx b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/palette/Palette.tsx index 1b54704320..1e0b57fc95 100644 --- a/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/palette/Palette.tsx +++ b/packages/diagrams/frontend/sirius-components-diagrams-reactflow/src/renderer/palette/Palette.tsx @@ -405,6 +405,8 @@ export const Palette = ({ x, y, diagramElementId, onDirectEditClick, isDiagramEl ))} {diagramPaletteToolComponents.map((component, index) => { const props: DiagramPaletteToolContributionComponentProps = { + x, + y, diagramElementId, key: index.toString(), };