Skip to content

Commit

Permalink
[2581] Add a new API to provide a reference position handler for cust…
Browse files Browse the repository at this point in the history
…om tool

Bug: #2581
Signed-off-by: Florian ROUËNÉ <florian.rouene@obeosoft.com>
  • Loading branch information
frouene authored and sbegaudeau committed Dec 5, 2023
1 parent 12e1e10 commit aae6c6d
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 23 deletions.
23 changes: 23 additions & 0 deletions doc/how-to/contribute-custom-tool.adoc
Expand Up @@ -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`.

Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -86,6 +83,8 @@ public class DiagramEventProcessor implements IDiagramEventProcessor {

private final DiagramEventFlux diagramEventFlux;

private final List<IDiagramInputReferencePositionProvider> diagramInputReferencePositionProviders;

private UUID currentRevisionId = UUID.randomUUID();

private String currentRevisionCause = DiagramRefreshedEventPayload.CAUSE_REFRESH;
Expand All @@ -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) {
Expand Down Expand Up @@ -194,27 +194,20 @@ 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);
}

/**
* A diagram is refresh if there is a semantic change or if there is a diagram layout change coming from this very
* diagram (not other diagrams).
*
* @param changeDescription
* The change description
* The change description
* @return <code>true</code> if the diagram should be refreshed, <code>false</code> otherwise
*/
public boolean shouldRefresh(ChangeDescription changeDescription) {
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -55,15 +56,18 @@ public class DiagramEventProcessorFactory implements IRepresentationEventProcess

private final IRepresentationPersistenceService representationPersistenceService;

private final List<IDiagramInputReferencePositionProvider> diagramInputReferencePositionProviders;

public DiagramEventProcessorFactory(RepresentationEventProcessorFactoryConfiguration configuration, IDiagramCreationService diagramCreationService,
List<IDiagramEventHandler> diagramEventHandlers, IRepresentationPersistenceService representationPersistenceService) {
List<IDiagramEventHandler> diagramEventHandlers, IRepresentationPersistenceService representationPersistenceService, List<IDiagramInputReferencePositionProvider> diagramInputReferencePositionProviders) {
this.representationSearchService = Objects.requireNonNull(configuration.getRepresentationSearchService());
this.diagramCreationService = Objects.requireNonNull(diagramCreationService);
this.diagramEventHandlers = Objects.requireNonNull(diagramEventHandlers);
this.representationPersistenceService = Objects.requireNonNull(representationPersistenceService);
this.subscriptionManagerFactory = Objects.requireNonNull(configuration.getSubscriptionManagerFactory());
this.representationDescriptionSearchService = Objects.requireNonNull(configuration.getRepresentationDescriptionSearchService());
this.representationRefreshPolicyRegistry = Objects.requireNonNull(configuration.getRepresentationRefreshPolicyRegistry());
this.diagramInputReferencePositionProviders = Objects.requireNonNull(diagramInputReferencePositionProviders);
}

@Override
Expand All @@ -89,6 +93,7 @@ public <T extends IRepresentationEventProcessor> Optional<T> createRepresentatio
.representationDescriptionSearchService(this.representationDescriptionSearchService)
.representationRefreshPolicyRegistry(this.representationRefreshPolicyRegistry)
.representationPersistenceService(this.representationPersistenceService)
.diagramInputReferencePositionProviders(this.diagramInputReferencePositionProviders)
.build();

IRepresentationEventProcessor diagramEventProcessor = new DiagramEventProcessor(parameters);
Expand Down
Expand Up @@ -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;

Expand All @@ -37,8 +38,10 @@ public record DiagramEventProcessorParameters(
IDiagramCreationService diagramCreationService,
IRepresentationDescriptionSearchService representationDescriptionSearchService,
IRepresentationRefreshPolicyRegistry representationRefreshPolicyRegistry,
IRepresentationPersistenceService representationPersistenceService
IRepresentationPersistenceService representationPersistenceService,
List<IDiagramInputReferencePositionProvider> diagramInputReferencePositionProviders
) {

public DiagramEventProcessorParameters {
Objects.requireNonNull(editingContext);
Objects.requireNonNull(diagramContext);
Expand All @@ -48,6 +51,7 @@ public record DiagramEventProcessorParameters(
Objects.requireNonNull(representationDescriptionSearchService);
Objects.requireNonNull(representationRefreshPolicyRegistry);
Objects.requireNonNull(representationPersistenceService);
Objects.requireNonNull(diagramInputReferencePositionProviders);
}

public static Builder newDiagramEventProcessorParameters() {
Expand All @@ -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;
Expand All @@ -78,6 +83,8 @@ public static final class Builder {

private IRepresentationPersistenceService representationPersistenceService;

private List<IDiagramInputReferencePositionProvider> diagramInputReferencePositionProviders;

private Builder() {
// Prevent instantiation
}
Expand Down Expand Up @@ -122,6 +129,11 @@ public Builder representationPersistenceService(IRepresentationPersistenceServic
return this;
}

public Builder diagramInputReferencePositionProviders(List<IDiagramInputReferencePositionProvider> diagramInputReferencePositionProviders) {
this.diagramInputReferencePositionProviders = Objects.requireNonNull(diagramInputReferencePositionProviders);
return this;
}

public DiagramEventProcessorParameters build() {
return new DiagramEventProcessorParameters(
this.editingContext,
Expand All @@ -131,7 +143,8 @@ public DiagramEventProcessorParameters build() {
this.diagramCreationService,
this.representationDescriptionSearchService,
this.representationRefreshPolicyRegistry,
this.representationPersistenceService
this.representationPersistenceService,
this.diagramInputReferencePositionProviders
);
}
}
Expand Down
@@ -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);
}
@@ -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;
}
}
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
Expand Up @@ -17,6 +17,8 @@ export interface DiagramPaletteToolContributionProps {
}

export interface DiagramPaletteToolContributionComponentProps {
x: number;
y: number;
diagramElementId: string;
key: string;
}
Expand Up @@ -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(),
};
Expand Down

0 comments on commit aae6c6d

Please sign in to comment.