Skip to content

Commit

Permalink
[2080] Add direct edit support on trees elements
Browse files Browse the repository at this point in the history
Bug: #2080
Signed-off-by: Michaël Charfadi <michael.charfadi@obeosoft.com>
  • Loading branch information
mcharfadi committed Jun 19, 2023
1 parent 3bbe4bc commit 20808ee
Show file tree
Hide file tree
Showing 27 changed files with 640 additions and 188 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Expand Up @@ -663,6 +663,7 @@ When closed, clicking on any of the views' icon will re-open the panel to make t
- https://github.com/eclipse-sirius/sirius-components/issues/1201[#1201] [charts] Prepare support for charts in Sirius Components
- https://github.com/eclipse-sirius/sirius-components/issues/1180[#1180] [diagram] Add support for the dynamic computation of the connector tools to control the tools displayed in the contextual menu.
- https://github.com/eclipse-sirius/sirius-components/issues/1212[#1212] [form] Add support for styling of the View DSL widgets
- https://github.com/eclipse-sirius/sirius-components/issues/2080[#2080] [tree] Add direct edit support on trees items.

== v2022.3.0

Expand Down
19 changes: 12 additions & 7 deletions integration-tests/cypress/e2e/project/edit/explorer.cy.js
Expand Up @@ -67,36 +67,41 @@ describe('/projects/:projectId/edit - Explorer', () => {
cy.getByTestId('selected').contains('robot');
});

const option = {
delay: 200,
};

it('can rename a selected document with simple click (direct edit)', () => {
cy.getByTestId('robot').click();
cy.getByTestId('selected').contains('robot');
cy.getByTestId('robot').click();
cy.getByTestId('robot').type('renamed-robot{enter}');
cy.getByTestId('renamed-robot').should('exist');
cy.getByTestId('robot').type('.renamed{enter}', option);
cy.getByTestId('robot.renamed').should('exist');
});

it('can rename a selected document by start typing (direct edit)', () => {
cy.getByTestId('robot').click();
cy.getByTestId('selected').contains('robot');
cy.getByTestId('robot').type('renamed-robot{enter}');
cy.getByTestId('renamed-robot').should('exist');
cy.getByTestId('robot').type('.renamed{enter}', option);
cy.getByTestId('robot.renamed').should('exist');
});

it('can cancel a direct edit with Escape', () => {
cy.getByTestId('robot').click();
cy.getByTestId('selected').contains('robot');
cy.getByTestId('robot').click();
cy.getByTestId('robot').type('renamed-robot{esc}');
cy.getByTestId('robot').type('.renamed{esc}', option);
cy.wait(500);
cy.getByTestId('robot').should('exist');
});

it('can apply a direct edit with focus lost', () => {
cy.getByTestId('robot').dblclick();
cy.getByTestId('Robot').should('exist');
cy.getByTestId('robot').click();
cy.getByTestId('robot').type('renamed-robot');
cy.getByTestId('robot').type('.renamed', option);
cy.getByTestId('Robot').click();
cy.getByTestId('renamed-robot').should('exist');
cy.getByTestId('robot.renamed').should('exist');
});

it('reveals a newly created diagram', () => {
Expand Down
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 Obeo.
* Copyright (c) 2022, 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
Expand Down Expand Up @@ -62,9 +62,9 @@ public class NavigationOperationHandlerTests {
private final IRepresentationMetadataSearchService representationMetadataSearchService = new IRepresentationMetadataSearchService.NoOp() {
@Override
public List<RepresentationMetadata> findAllByTargetObjectId(IEditingContext editingContext, String targetObjectId) {
var firstRepresentationMetadata = new RepresentationMetadata(FIRST_DIAGRAM_ID, Diagram.KIND, FIRST_DIAGRAM_LABEL, FIRST_DIAGRAM_DESCRIPTION_ID, targetObjectId);
var secondRepresentationMetadata = new RepresentationMetadata(SECOND_DIAGRAM_ID, Diagram.KIND, SECOND_DIAGRAM_LABEL, FIRST_DIAGRAM_DESCRIPTION_ID, targetObjectId);
var thirdRepresentationMetadata = new RepresentationMetadata(THIRD_DIAGRAM_ID, Diagram.KIND, THIRD_DIAGRAM_LABEL, SECOND_DIAGRAM_DESCRIPTION_ID, targetObjectId);
var firstRepresentationMetadata = new RepresentationMetadata(FIRST_DIAGRAM_ID, Diagram.KIND, FIRST_DIAGRAM_LABEL, FIRST_DIAGRAM_DESCRIPTION_ID);
var secondRepresentationMetadata = new RepresentationMetadata(SECOND_DIAGRAM_ID, Diagram.KIND, SECOND_DIAGRAM_LABEL, FIRST_DIAGRAM_DESCRIPTION_ID);
var thirdRepresentationMetadata = new RepresentationMetadata(THIRD_DIAGRAM_ID, Diagram.KIND, THIRD_DIAGRAM_LABEL, SECOND_DIAGRAM_DESCRIPTION_ID);
return List.of(firstRepresentationMetadata, secondRepresentationMetadata, thirdRepresentationMetadata);
}
};
Expand Down
Expand Up @@ -20,8 +20,6 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import jakarta.annotation.PreDestroy;

import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessor;
import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessorFactory;
import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessorRegistry;
Expand Down Expand Up @@ -105,7 +103,6 @@ public synchronized Optional<IEditingContextEventProcessor> getOrCreateEditingCo
}
}
}

return optionalEditingContextEventProcessor;
}

Expand All @@ -116,7 +113,6 @@ public void disposeEditingContextEventProcessor(String editingContextId) {
this.logger.trace("Editing context event processors count: {}", this.editingContextEventProcessors.size());
}

@PreDestroy
public void dispose() {
this.logger.debug("Shutting down all the editing context event processors");

Expand Down
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022 Obeo.
* Copyright (c) 2022, 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
Expand Down Expand Up @@ -29,14 +29,11 @@ public class RepresentationMetadata {

private final String descriptionId;

private final String targetObjectId;

public RepresentationMetadata(String id, String kind, String label, String descriptionId, String targetObjectId) {
public RepresentationMetadata(String id, String kind, String label, String descriptionId) {
this.id = Objects.requireNonNull(id);
this.kind = Objects.requireNonNull(kind);
this.label = Objects.requireNonNull(label);
this.descriptionId = Objects.requireNonNull(descriptionId);
this.targetObjectId = targetObjectId;
}

public String getId() {
Expand All @@ -55,13 +52,9 @@ public String getDescriptionId() {
return this.descriptionId;
}

public String getTargetObjectId() {
return this.targetObjectId;
}

@Override
public String toString() {
String pattern = "{0} '{'id: {1}, kind: {2}, label: {3}, descriptionId: {4}, targetObjectId: {5}'}'";
return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.kind, this.label, this.descriptionId, this.targetObjectId);
String pattern = "{0} '{'id: {1}, kind: {2}, label: {3}, descriptionId: {4}'}'";
return MessageFormat.format(pattern, this.getClass().getSimpleName(), this.id, this.kind, this.label, this.descriptionId);
}
}
Expand Up @@ -15,7 +15,6 @@
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher;
import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessorRegistry;
Expand All @@ -24,7 +23,6 @@
import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates;
import org.eclipse.sirius.components.graphql.api.LocalContextConstants;
import org.eclipse.sirius.components.representations.IRepresentation;
import org.eclipse.sirius.components.representations.ISemanticRepresentation;
import org.eclipse.sirius.web.services.api.representations.IRepresentationService;
import org.eclipse.sirius.web.services.api.representations.RepresentationDescriptor;

Expand Down Expand Up @@ -66,22 +64,20 @@ public DataFetcherResult<RepresentationMetadata> get(DataFetchingEnvironment env

Map<String, Object> localContext = new HashMap<>(environment.getLocalContext());
localContext.put(LocalContextConstants.REPRESENTATION_ID, representationId);

// Search among the active representations first. They are already loaded in memory and include transient
// representations.
// @formatter:off
var representationMetadata = this.editingContextEventProcessorRegistry.getEditingContextEventProcessors().stream()
.flatMap(editingContextEventProcessor -> editingContextEventProcessor.getRepresentationEventProcessors().stream())
.filter(editingContextEventProcessor -> editingContextEventProcessor.getRepresentation().getId().equals(representationId))
.map(IRepresentationEventProcessor::getRepresentation)
.filter(ISemanticRepresentation.class::isInstance)
.map(ISemanticRepresentation.class::cast)
.map((ISemanticRepresentation representation) -> {
.filter(IRepresentation.class::isInstance)
.map(IRepresentation.class::cast)
.map((IRepresentation representation) -> {
return new RepresentationMetadata(representation.getId(),
representation.getKind(),
representation.getLabel(),
representation.getDescriptionId(),
representation.getTargetObjectId());
representation.getDescriptionId());
})
.findFirst();
// @formatter:on
Expand All @@ -101,14 +97,7 @@ public DataFetcherResult<RepresentationMetadata> get(DataFetchingEnvironment env
}

private RepresentationMetadata toRepresentationMetadata(IRepresentation representation) {
// @formatter:off
String targetObjectId = Optional.of(representation)
.filter(ISemanticRepresentation.class::isInstance)
.map(ISemanticRepresentation.class::cast)
.map(ISemanticRepresentation::getTargetObjectId)
.orElse(null);
// @formatter:on
return new RepresentationMetadata(representation.getId(), representation.getKind(), representation.getLabel(), representation.getDescriptionId(), targetObjectId);
return new RepresentationMetadata(representation.getId(), representation.getKind(), representation.getLabel(), representation.getDescriptionId());
}

}
Expand Up @@ -15,13 +15,11 @@
import java.util.Base64;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher;
import org.eclipse.sirius.components.core.RepresentationMetadata;
import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates;
import org.eclipse.sirius.components.representations.IRepresentation;
import org.eclipse.sirius.components.representations.ISemanticRepresentation;
import org.eclipse.sirius.web.services.api.representations.IRepresentationService;
import org.eclipse.sirius.web.services.api.representations.RepresentationDescriptor;

Expand Down Expand Up @@ -90,14 +88,7 @@ public Connection<RepresentationMetadata> get(DataFetchingEnvironment environmen
}

private RepresentationMetadata toRepresentationMetadata(IRepresentation representation) {
// @formatter:off
String targetObjectId = Optional.of(representation)
.filter(ISemanticRepresentation.class::isInstance)
.map(ISemanticRepresentation.class::cast)
.map(ISemanticRepresentation::getTargetObjectId)
.orElse(null);
// @formatter:on
return new RepresentationMetadata(representation.getId(), representation.getKind(), representation.getLabel(), representation.getDescriptionId(), targetObjectId);
return new RepresentationMetadata(representation.getId(), representation.getKind(), representation.getLabel(), representation.getDescriptionId());
}

}
Expand Up @@ -18,18 +18,24 @@
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;

import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher;
import org.eclipse.sirius.components.collaborative.api.IEditingContextEventProcessorRegistry;
import org.eclipse.sirius.components.collaborative.dto.GetRepresentationDescriptionInput;
import org.eclipse.sirius.components.collaborative.dto.GetRepresentationDescriptionPayload;
import org.eclipse.sirius.components.collaborative.forms.PropertiesEventProcessorFactory;
import org.eclipse.sirius.components.collaborative.trees.TreeEventProcessorFactory;
import org.eclipse.sirius.components.core.RepresentationMetadata;
import org.eclipse.sirius.components.forms.description.FormDescription;
import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates;
import org.eclipse.sirius.components.graphql.api.LocalContextConstants;
import org.eclipse.sirius.components.representations.Failure;
import org.eclipse.sirius.components.representations.GetOrCreateRandomIdProvider;
import org.eclipse.sirius.components.representations.IRepresentationDescription;
import org.eclipse.sirius.components.representations.IStatus;
import org.eclipse.sirius.components.representations.VariableManager;
import org.eclipse.sirius.components.trees.description.TreeDescription;

import graphql.schema.DataFetchingEnvironment;
import reactor.core.publisher.Mono;
Expand All @@ -42,6 +48,8 @@
@QueryDataFetcher(type = "RepresentationMetadata", field = "description")
public class RepresentationMetadataDescriptionDataFetcher implements IDataFetcherWithFieldCoordinates<CompletableFuture<IRepresentationDescription>> {

static final BiFunction<VariableManager, String, IStatus> FAKE_RENAME_HANDLER = (a, b) -> new Failure("");

// @formatter:off
private static final IRepresentationDescription FAKE_DETAILS_DESCRIPTION = FormDescription.newFormDescription(PropertiesEventProcessorFactory.DETAILS_VIEW_ID)
.label(PropertiesEventProcessorFactory.DETAILS_VIEW_ID)
Expand All @@ -53,8 +61,28 @@ public class RepresentationMetadataDescriptionDataFetcher implements IDataFetche
.build();
// @formatter:on

// @formatter:off
private static final IRepresentationDescription FAKE_DETAILS_TREE = TreeDescription.newTreeDescription(TreeEventProcessorFactory.DETAILS_VIEW_ID)
.label("Explorer")
.idProvider(new GetOrCreateRandomIdProvider())
.treeItemIdProvider(variableManager -> TreeEventProcessorFactory.DETAILS_VIEW_ID)
.kindProvider(variableManager -> TreeEventProcessorFactory.DETAILS_VIEW_ID)
.labelProvider(variableManager -> TreeEventProcessorFactory.DETAILS_VIEW_ID)
.imageURLProvider(variableManager -> TreeEventProcessorFactory.DETAILS_VIEW_ID)
.editableProvider(variableManager -> null)
.deletableProvider(variableManager -> null)
.elementsProvider(variableManager -> null)
.hasChildrenProvider(variableManager -> null)
.childrenProvider(variableManager -> null)
.canCreatePredicate(variableManager -> true)
.deleteHandler(variableManager -> null)
.renameHandler(FAKE_RENAME_HANDLER)
.build();
// @formatter:on

private final IEditingContextEventProcessorRegistry editingContextEventProcessorRegistry;


public RepresentationMetadataDescriptionDataFetcher(IEditingContextEventProcessorRegistry editingContextEventProcessorRegistry) {
this.editingContextEventProcessorRegistry = Objects.requireNonNull(editingContextEventProcessorRegistry);
}
Expand All @@ -64,23 +92,27 @@ public CompletableFuture<IRepresentationDescription> get(DataFetchingEnvironment
CompletableFuture<IRepresentationDescription> result = Mono.<IRepresentationDescription>empty().toFuture();

RepresentationMetadata representationMetadata = environment.getSource();
if (Objects.equals(PropertiesEventProcessorFactory.DETAILS_VIEW_ID, representationMetadata.getDescriptionId())) {
if (Objects.equals(PropertiesEventProcessorFactory.DETAILS_VIEW_ID, representationMetadata.getDescriptionId()) || (Objects.equals(TreeEventProcessorFactory.DETAILS_VIEW_ID, representationMetadata.getDescriptionId()))) {
/*
* The FormDescription used for the details view can not be found by
* IRepresentationDescriptionSearchService, but we can get away by returning a fake one with the same id as
* no GraphQL query actually needs to see its content. We need to return *something* with the correct id
* only to allow GraphQL resolution to continue on queries like "completionProposals" defined on
* FormDescription.
*/
result = Mono.just(FAKE_DETAILS_DESCRIPTION).toFuture();
if (Objects.equals(PropertiesEventProcessorFactory.DETAILS_VIEW_ID, representationMetadata.getDescriptionId())) {
result = Mono.just(FAKE_DETAILS_DESCRIPTION).toFuture();
}
if (Objects.equals(TreeEventProcessorFactory.DETAILS_VIEW_ID, representationMetadata.getDescriptionId())) {
result = Mono.just(FAKE_DETAILS_TREE).toFuture();
}
} else {
Map<String, Object> localContext = environment.getLocalContext();

String editingContextId = Optional.ofNullable(localContext.get(LocalContextConstants.EDITING_CONTEXT_ID)).map(Object::toString).orElse(null);
String representationId = Optional.ofNullable(localContext.get(LocalContextConstants.REPRESENTATION_ID)).map(Object::toString).orElse(null);
if (editingContextId != null && representationId != null) {
GetRepresentationDescriptionInput input = new GetRepresentationDescriptionInput(UUID.randomUUID(), editingContextId, representationId);

// @formatter:off
result = this.editingContextEventProcessorRegistry.dispatchEvent(input.editingContextId(), input)
.filter(GetRepresentationDescriptionPayload.class::isInstance)
Expand Down
Expand Up @@ -19,12 +19,14 @@
import fr.obeo.dsl.designer.sample.flow.FlowFactory;
import fr.obeo.dsl.designer.sample.flow.Processor;
import fr.obeo.dsl.designer.sample.flow.System;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;

import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.sirius.components.collaborative.api.IRepresentationPersistenceService;
Expand Down Expand Up @@ -133,7 +135,7 @@ private Optional<RepresentationMetadata> initializeFlowProject(IEditingContext e
Diagram diagram = this.diagramCreationService.create(topographyDiagram.getLabel(), semanticTarget, topographyDiagram, editingContext);
this.representationPersistenceService.save(editingContext, diagram);

result = Optional.of(new RepresentationMetadata(diagram.getId(), diagram.getKind(), diagram.getLabel(), diagram.getDescriptionId(), diagram.getTargetObjectId()));
result = Optional.of(new RepresentationMetadata(diagram.getId(), diagram.getKind(), diagram.getLabel(), diagram.getDescriptionId()));
}
} catch (IOException exception) {
this.logger.warn(exception.getMessage(), exception);
Expand Down

0 comments on commit 20808ee

Please sign in to comment.