diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index f5b135e43..a553de212 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -102,6 +102,7 @@ Now the _end_ keyword is not displayed anymore in the label of these graphical n - https://github.com/eclipse-syson/syson/issues/2053[#2053] [diagrams] Prevent incoming and outgoing graphical edges of a graphical `ForkNode` or `JoinNode` to point empty space. - https://github.com/eclipse-syson/syson/issues/2059[#2059] [diagrams] Fix an issue where the _Add existing elements_ tool was not working correctly on the _action flow_ compartment of `ActionUsage` graphical nodes. - https://github.com/eclipse-syson/syson/issues/2043[#2043] [explorer] Prevent user libraries from being moved to the root of the project when a child is created in it. +- https://github.com/eclipse-syson/syson/issues/2056[#2056] [diagams] Fix the user feedback when dropping an `Element` which is already exposed on a diagram. === Improvements diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVDropFromExplorerTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVDropFromExplorerTests.java index fb94e2a0c..49945f4b4 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVDropFromExplorerTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVDropFromExplorerTests.java @@ -20,6 +20,7 @@ import java.time.Duration; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.UUID; @@ -28,13 +29,20 @@ import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramEventInput; import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramRefreshedEventPayload; +import org.eclipse.sirius.components.collaborative.diagrams.dto.InvokeSingleClickOnDiagramElementToolInput; +import org.eclipse.sirius.components.collaborative.diagrams.dto.InvokeSingleClickOnDiagramElementToolSuccessPayload; import org.eclipse.sirius.components.collaborative.dto.CreateChildInput; import org.eclipse.sirius.components.collaborative.dto.CreateChildSuccessPayload; import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.core.api.IFeedbackMessageService; import org.eclipse.sirius.components.core.api.IIdentityService; import org.eclipse.sirius.components.core.api.IObjectSearchService; import org.eclipse.sirius.components.diagrams.Diagram; import org.eclipse.sirius.components.diagrams.Node; +import org.eclipse.sirius.components.diagrams.ViewModifier; +import org.eclipse.sirius.components.diagrams.tests.graphql.InvokeSingleClickOnDiagramElementToolMutationRunner; +import org.eclipse.sirius.components.diagrams.tests.graphql.PaletteQueryRunner; +import org.eclipse.sirius.components.diagrams.tests.navigation.DiagramNavigator; import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionSuccessPayload; import org.eclipse.sirius.components.representations.MessageLevel; import org.eclipse.sirius.components.view.emf.diagram.IDiagramIdProvider; @@ -42,7 +50,6 @@ import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; import org.eclipse.syson.AbstractIntegrationTests; import org.eclipse.syson.GivenSysONServer; -import org.eclipse.syson.SysONTestsProperties; import org.eclipse.syson.application.controllers.diagrams.checkers.CheckDiagramElementCount; import org.eclipse.syson.application.controllers.diagrams.checkers.CheckNodeOnDiagram; import org.eclipse.syson.application.controllers.diagrams.testers.DropFromExplorerTester; @@ -79,7 +86,7 @@ * @author gdaniel */ @Transactional -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { SysONTestsProperties.NO_DEFAULT_LIBRARIES_PROPERTY }) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class GVDropFromExplorerTests extends AbstractIntegrationTests { @Autowired @@ -112,8 +119,18 @@ public class GVDropFromExplorerTests extends AbstractIntegrationTests { @Autowired private CreateChildMutationRunner createChildMutationRunner; + @Autowired + private PaletteQueryRunner paletteQueryRunner; + + @Autowired + private InvokeSingleClickOnDiagramElementToolMutationRunner invokeSingleClickOnDiagramElementToolMutationRunner; + + @Autowired + private IFeedbackMessageService feedbackMessageService; + private final IDescriptionNameGenerator descriptionNameGenerator = new SDVDescriptionNameGenerator(); + private Flux givenSubscriptionToDiagram() { var diagramEventInput = new DiagramEventInput(UUID.randomUUID(), GeneralViewAddExistingElementsTestProjectData.EDITING_CONTEXT_ID, @@ -198,9 +215,9 @@ public void dropFromExplorerOnEmptyDiagram() { .verify(Duration.ofSeconds(10)); } + @DisplayName("GIVEN an Element with no declared name but having a declared short name, WHEN drag and dropping this element on a diagram, THEN graphical node should be created") @GivenSysONServer({ GeneralViewAddExistingElementsTestProjectData.SCRIPT_PATH }) @Test - @DisplayName("GIVEN an Element with no declared name but having a declared short name, WHEN drag and dropping this element on a diagram, THEN graphical node should be created") public void dropFromExplorerShortNameOnlyOnEmptyDiagram() { var flux = this.givenSubscriptionToDiagram(); @@ -358,7 +375,7 @@ public void dropFromExplorerTwiceShouldNotExposeElementTwice() { List messages = JsonPath.read(result.data(), "$.data.dropOnDiagram.messages[*]"); assertThat(messages).as("We should receive at least one message when dropping an already visible element").hasSizeGreaterThanOrEqualTo(1); String messageBody = JsonPath.read(result.data(), "$.data.dropOnDiagram.messages[0].body"); - assertThat(messageBody).isEqualTo("The element part1 is already visible in its parent General View"); + assertThat(messageBody).isEqualTo("The element part1 is already visible in its parent Package 1"); String messageLevel = JsonPath.read(result.data(), "$.data.dropOnDiagram.messages[0].level"); assertThat(messageLevel).isEqualTo(MessageLevel.WARNING.toString()); }; @@ -376,6 +393,128 @@ public void dropFromExplorerTwiceShouldNotExposeElementTwice() { .verify(Duration.ofSeconds(10)); } + @DisplayName("GIVEN a diagram WHEN dropping a semantic element from the explorer on various targets THEN the user gets the appropriate feedback message") + @GivenSysONServer({ GeneralViewAddExistingElementsTestProjectData.SCRIPT_PATH }) + @Test + public void dropFromExplorerFeedback() { + AtomicReference diagram = new AtomicReference<>(); + AtomicReference packageNodeId = new AtomicReference<>(); + AtomicReference attributeNodeId = new AtomicReference<>(); + AtomicReference hideToolId = new AtomicReference<>(); + + String package1Id = GeneralViewAddExistingElementsTestProjectData.SemanticIds.PACKAGE1_ID; + String attributeDefinition1Id = GeneralViewAddExistingElementsTestProjectData.SemanticIds.ATTRIBUTE_DEFINITION_1_ID; + + var flux = this.givenSubscriptionToDiagram(); + + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set); + + Runnable dropPackageOnDiagramBackground = () -> this.dropFromExplorer(diagram, null, package1Id, Optional.empty()); + + Consumer diagramWithPackageNodeConsumer = assertRefreshedDiagramThat(newDiagram -> { + new CheckDiagramElementCount(this.diagramComparator).hasNewNodeCount(1).check(diagram.get(), newDiagram); + var packageNode = new DiagramNavigator(newDiagram).nodeWithTargetObjectId(package1Id).getNode(); + assertThat(packageNode).isNotNull(); + packageNodeId.set(packageNode.getId()); + diagram.set(newDiagram); + }); + + Runnable dropAttributeOnPackage = () -> this.dropFromExplorer(diagram, packageNodeId.get(), attributeDefinition1Id, Optional.empty()); + + Consumer diagramWithAttributeNodeConsumer = assertRefreshedDiagramThat(newDiagram -> { + // 3 new nodes: AttributeDefinition container and its 2 compartments + new CheckDiagramElementCount(this.diagramComparator).hasNewNodeCount(3).check(diagram.get(), newDiagram); + var attributeNode = new DiagramNavigator(newDiagram).nodeWithTargetObjectId(attributeDefinition1Id).getNode(); + assertThat(attributeNode).isNotNull().extracting(Node::getState).isEqualTo(ViewModifier.Normal); + attributeNodeId.set(attributeNode.getId()); + diagram.set(newDiagram); + }); + + Runnable getHideTool = () -> hideToolId.set(this.getQuickToolIdByLabel(diagram.get().getId(), attributeNodeId.get(), "Hide")); + + Runnable dropAttributeOnDiagram = () -> this.dropFromExplorer(diagram, + null, attributeDefinition1Id, + Optional.of("The element AttributeDefinition1 is already visible in its parent Package1")); + + Consumer diagramNotChangeConsumer = assertRefreshedDiagramThat(newDiagram -> { + new CheckDiagramElementCount(this.diagramComparator).check(diagram.get(), newDiagram); + diagram.set(newDiagram); + }); + + Runnable hideAttributeNode = () -> { + var input = new InvokeSingleClickOnDiagramElementToolInput(UUID.randomUUID(), GeneralViewAddExistingElementsTestProjectData.EDITING_CONTEXT_ID, diagram.get().getId(), + List.of(attributeNodeId.get()), hideToolId.get(), 0, 0, List.of()); + var result = this.invokeSingleClickOnDiagramElementToolMutationRunner.run(input); + String typename = JsonPath.read(result.data(), "$.data.invokeSingleClickOnDiagramElementTool.__typename"); + assertThat(typename).isEqualTo(InvokeSingleClickOnDiagramElementToolSuccessPayload.class.getSimpleName()); + }; + + Consumer atributeNodeHiddenConsumer = assertRefreshedDiagramThat(newDiagram -> { + new CheckDiagramElementCount(this.diagramComparator).check(diagram.get(), newDiagram); + var attributeNode = new DiagramNavigator(newDiagram).nodeWithTargetObjectId(attributeDefinition1Id).getNode(); + assertThat(attributeNode).isNotNull().extracting(Node::getState).isEqualTo(ViewModifier.Hidden); + diagram.set(newDiagram); + }); + + Runnable dropAttributeOnDiagramNoMessage = () -> this.dropFromExplorer(diagram, null, attributeDefinition1Id, Optional.empty()); + + Runnable dropAttributeOnPackageAgain = () -> this.dropFromExplorer(diagram, packageNodeId.get(), attributeDefinition1Id, Optional.empty()); + + Consumer diagramWithAttributeNodeRevealedConsumer = assertRefreshedDiagramThat(newDiagram -> { + new CheckDiagramElementCount(this.diagramComparator).check(diagram.get(), newDiagram); + var attributeNode = new DiagramNavigator(newDiagram).nodeWithTargetObjectId(GeneralViewAddExistingElementsTestProjectData.SemanticIds.ATTRIBUTE_DEFINITION_1_ID).getNode(); + assertThat(attributeNode).isNotNull().extracting(Node::getState).isEqualTo(ViewModifier.Normal); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(dropPackageOnDiagramBackground) + .consumeNextWith(diagramWithPackageNodeConsumer) + .then(dropAttributeOnPackage) + .consumeNextWith(diagramWithAttributeNodeConsumer) + .then(getHideTool) + .then(dropAttributeOnDiagram) + .consumeNextWith(diagramNotChangeConsumer) + .then(hideAttributeNode) + .consumeNextWith(atributeNodeHiddenConsumer) + .then(dropAttributeOnDiagramNoMessage) + .consumeNextWith(diagramNotChangeConsumer) + .then(dropAttributeOnPackageAgain) + .consumeNextWith(diagramWithAttributeNodeRevealedConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + private void dropFromExplorer(AtomicReference diagram, String targetId, String elementId, Optional expectedWarning) { + // Workaround: clear the messages left by the previous tool's execution + this.feedbackMessageService.getFeedbackMessages().clear(); + var result = this.dropFromExplorerTester.dropFromExplorer(GeneralViewAddExistingElementsTestProjectData.EDITING_CONTEXT_ID, diagram, + targetId, elementId); + List messages = JsonPath.read(result.data(), "$.data.dropOnDiagram.messages[*]"); + if (expectedWarning.isPresent()) { + assertThat(messages).hasSize(1); + String messageBody = JsonPath.read(result.data(), "$.data.dropOnDiagram.messages[0].body"); + assertThat(messageBody).isEqualTo(expectedWarning.get()); + String messageLevel = JsonPath.read(result.data(), "$.data.dropOnDiagram.messages[0].level"); + assertThat(messageLevel).isEqualTo(MessageLevel.WARNING.toString()); + } else { + assertThat(messages).isEmpty(); + } + } + + private String getQuickToolIdByLabel(String diagramId, String nodeId, String toolName) { + Map variables = Map.of( + "editingContextId", GeneralViewAddExistingElementsTestProjectData.EDITING_CONTEXT_ID, + "representationId", diagramId, + "diagramElementIds", List.of(nodeId)); + var result = this.paletteQueryRunner.run(variables); + List labels = JsonPath.read(result.data(), "$.data.viewer.editingContext.representation.description.palette.quickAccessTools[*].label"); + assertThat(labels).contains(toolName); + int toolIndex = labels.indexOf(toolName); + List ids = JsonPath.read(result.data(), "$.data.viewer.editingContext.representation.description.palette.quickAccessTools[*].id"); + return ids.get(toolIndex); + } + @GivenSysONServer({ GeneralViewEmptyTestProjectData.SCRIPT_PATH }) @Test public void dropLibraryPackageFromExplorerOnDiagram() { diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/DropFromExplorerTester.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/DropFromExplorerTester.java index e074b9e96..f0c7a07bc 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/DropFromExplorerTester.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/testers/DropFromExplorerTester.java @@ -23,7 +23,6 @@ import org.eclipse.sirius.components.collaborative.diagrams.dto.DropOnDiagramInput; import org.eclipse.sirius.components.collaborative.diagrams.dto.DropOnDiagramSuccessPayload; import org.eclipse.sirius.components.diagrams.Diagram; -import org.eclipse.sirius.components.diagrams.tests.navigation.DiagramNavigator; import org.eclipse.sirius.components.graphql.tests.api.GraphQLResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -54,12 +53,9 @@ public void dropFromExplorerOnDiagramElement(String projectId, AtomicReference diagram, String targetNodeId, String semanticElementId) { - DiagramNavigator diagramNavigator = new DiagramNavigator(diagram.get()); - final String targetId; + String targetId = targetNodeId; if (targetNodeId == null) { targetId = diagram.get().getId(); - } else { - targetId = diagramNavigator.nodeWithId(targetNodeId).getNode().getId(); } var dropOnDiagramInput = new DropOnDiagramInput( UUID.randomUUID(), diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/GeneralViewAddExistingElementsTestProjectData.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/GeneralViewAddExistingElementsTestProjectData.java index 986d90729..60295c8d1 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/GeneralViewAddExistingElementsTestProjectData.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/data/GeneralViewAddExistingElementsTestProjectData.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2025 Obeo. + * Copyright (c) 2025, 2026 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 @@ -39,6 +39,10 @@ public static final class SemanticIds { public static final String PACKAGE_1_ID = "8d4123ac-3ac5-412d-90f2-49282b923003"; + public static final String PACKAGE1_ID = "98a777a3-15df-400f-a8d6-b5033e37e082"; + + public static final String ATTRIBUTE_DEFINITION_1_ID = "f42f0e49-8f00-49b4-b1ce-f4b0a98d3eb8"; + public static final String GENERAL_VIEW_VIEW_USAGE_ID = "b67872fa-5900-48d3-88f0-e6ced193c8ec"; public static final String PART_1_ELEMENT_ID = "67a57df8-2995-41ea-a838-dfb3a9a8ee7f"; diff --git a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationDndService.java b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationDndService.java index 93ecb1d7e..752ee51a9 100644 --- a/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationDndService.java +++ b/backend/services/syson-diagram-services/src/main/java/org/eclipse/syson/diagram/services/DiagramMutationDndService.java @@ -20,6 +20,7 @@ import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.eclipse.sirius.components.collaborative.diagrams.DiagramContext; @@ -27,6 +28,7 @@ import org.eclipse.sirius.components.collaborative.diagrams.DiagramServices; import org.eclipse.sirius.components.collaborative.diagrams.api.IDiagramService; import org.eclipse.sirius.components.core.api.IEditingContext; +import org.eclipse.sirius.components.diagrams.Diagram; import org.eclipse.sirius.components.diagrams.Node; import org.eclipse.sirius.components.diagrams.ViewCreationRequest; import org.eclipse.sirius.components.diagrams.ViewDeletionRequest; @@ -314,21 +316,7 @@ private void dropElementFromExplorerInTarget(Element sourceElement, Element targ this.utilService.setFeatureTyping(usage, definition); } else if (this.modelQueryElementService.isExposable(sourceElement)) { if (this.modelQueryElementService.isExposed(sourceElement, this.diagramQueryElementService.getViewUsage(editingContext, diagramContext, selectedNode))) { - var parentId = this.diagramQueryElementService.getGraphicalParentId(diagramContext, selectedNode); - var descriptionId = this.diagramQueryElementService.getNodeDescriptionId(sourceElement, diagramContext.diagram(), editingContext); - if (descriptionId.isPresent()) { - var nodeId = new NodeIdProvider().getNodeId(parentId, - descriptionId.get(), - NodeContainmentKind.CHILD_NODE, - this.siriusWebCoreServices.identityService().getId(sourceElement)); - this.diagramQueryElementService.findNodeById(diagramContext.diagram(), nodeId).ifPresent(node -> { - if (node.getState().equals(ViewModifier.Hidden)) { - diagramContext.diagramEvents().add(new HideDiagramElementEvent(Set.of(nodeId), false)); - } else { - this.logAlreadyVisibleMessage(sourceElement, targetElement); - } - }); - } + this.handleExposedElement(sourceElement, targetElement, editingContext, diagramContext, selectedNode); } else { Node newSelectedNode = selectedNode; if (selectedNode == null) { @@ -366,8 +354,105 @@ private void dropElementFromExplorerInTarget(Element sourceElement, Element targ } } + /** + * Handle the case where the semantic element dropped is already exposed on the diagram. There are four distinct + * cases to handle: + *
    + *
  1. The element is dropped on a diagram element in which it is already represented: + *
      + *
    1. if this representation was hidden, reveal it;
    2. + *
    3. otherwise display a message "the element is already visible". + *
    4. + *
    + *
  2. + *
  3. The element is dropped on a diagram element where is is not already represented: + *
      + *
    1. if the element is actually visible elsewhere, display a message;
    2. + *
    3. otherwise do nothing.
    4. + *
    + *
  4. + *
+ * + * @param sourceElement + * the source element to drop + * @param targetElement + * the target of the drop + * @param editingContext + * the {@link IEditingContext} of the tool. It corresponds to a variable accessible from the variable + * manager. + * @param diagramContext + * the {@link DiagramContext} of the tool. It corresponds to a variable accessible from the variable + * manager. + * @param selectedNode + * the selected node on which the service has been called (may be null if the tool has been called from + * the diagram). It corresponds to a variable accessible from the variable manager. + * @param convertedNodes + * the map of all existing node descriptions in the DiagramDescription of this Diagram. It corresponds to + * a variable accessible from the variable manager. + */ + private void handleExposedElement(Element sourceElement, Element targetElement, IEditingContext editingContext, DiagramContext diagramContext, Node selectedNode) { + String droppedElementId = this.siriusWebCoreServices.identityService().getId(sourceElement); + + // Are there any which are actually inside the drop target? + // Is the dropped element already represented inside the drop target? + Set subNodes = this.findSubNodesBySemanticElementId(diagramContext, selectedNode, droppedElementId); + if (!subNodes.isEmpty()) { + if (subNodes.stream().anyMatch(n -> !n.getState().equals(ViewModifier.Hidden))) { + // The semantic element is already visible in the drop context + this.logAlreadyVisibleMessage(sourceElement, targetElement); + } else { + // All are hidden, reveal them (all) + Set nodeIds = subNodes.stream().map(Node::getId).collect(Collectors.toSet()); + diagramContext.diagramEvents().add(new HideDiagramElementEvent(nodeIds, false)); + } + } else { + // If the dropped element is not represented *in the target*, is it represented elsewhere? + var droppedElementNodes = new NodeFinder(diagramContext.diagram()).getAllNodesMatching(n -> n.getTargetObjectId().equals(droppedElementId)); + var visibleNodes = droppedElementNodes.stream().filter(n -> !n.getState().equals(ViewModifier.Hidden)).toList(); + if (!visibleNodes.isEmpty()) { + String parentTargetObjectId = this.findParentTargetObjectId(diagramContext, visibleNodes.get(0)); + this.siriusWebCoreServices.objectSearchService() + .getObject(editingContext, parentTargetObjectId) + .filter(Element.class::isInstance) + .map(Element.class::cast) + .ifPresent(parentElement -> this.logAlreadyVisibleMessage(sourceElement, parentElement)); + } else { + // The semantic is already *visible* elsewhere on the diagram, so nothing to reveal. + // Do nothing on purpose (for the moment). + } + } + } + + private String findParentTargetObjectId(DiagramContext diagramContext, Node visibleNode) { + Object parentElement = new NodeFinder(diagramContext.diagram()).getParent(visibleNode); + String parentTargetObjectId = null; + if (parentElement instanceof Diagram diagram) { + parentTargetObjectId = diagram.getTargetObjectId(); + } else if (parentElement instanceof Node node) { + parentTargetObjectId = node.getTargetObjectId(); + } + return parentTargetObjectId; + } + + private Set findSubNodesBySemanticElementId(DiagramContext diagramContext, Node parenNode, String semanticElementId) { + Set subnodes = new HashSet<>(); + if (parenNode != null) { + subnodes.addAll(parenNode.getChildNodes()); + subnodes.addAll(parenNode.getBorderNodes()); + } else { + subnodes.addAll(diagramContext.diagram().getNodes()); + } + subnodes.removeIf(node -> !Objects.equals(node.getTargetObjectId(), semanticElementId)); + return subnodes; + } + private void logAlreadyVisibleMessage(Element element, Element parent) { - String errorMessage = MessageFormat.format("The element {0} is already visible in its parent {1}", element.getName(), parent.getName()); + // For the diagram itself, show the name of the corresponding semantic element instead of e.g. "view1" + String parentName = parent.getName(); + if (parent instanceof ViewUsage viewUsage && viewUsage.getOwner() != null) { + parentName = viewUsage.getOwner().getName(); + } + String errorMessage = MessageFormat.format("The element {0} is already visible in its parent {1}", element.getName(), parentName); this.logger.warn(errorMessage); this.siriusWebCoreServices.feedbackMessageService().addFeedbackMessage(new Message(errorMessage, MessageLevel.WARNING)); } diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.3.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.3.0.adoc index 6a8b34dba..0dca18d89 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.3.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.3.0.adoc @@ -39,6 +39,7 @@ Now the _end_ keyword is not displayed anymore in the label of these graphical n ** The `ForkNode` and `JoinNode` graphical nodes style have been updated and are restricted to horizontal resizing. + Consequently, their entire footprint is filled with black, ensuring that all incoming and outgoing edges maintain a valid connection point. ** Fix an issue where the _Add existing elements_ tool was not working correctly on the _action flow_ compartment of `ActionUsage` graphical nodes. +** Fix the user feedback when dropping an `Element` from the _Explorer_ view which is already exposed on a diagram. * In textual import/export: @@ -84,9 +85,8 @@ part system { } ---- -** Fix the textual import so `Relationships` can be annotated. -Such as in : - +** Fix the textual import so `Relationships` can be annotated such as in: ++ [source] ---- package Test { @@ -106,7 +106,7 @@ package Test { ** Fix the export of `LiteralString` with backslash characters. ** Fix textual import of `LiteralString` containing escaped backslash. In the following example `attribute a1 = "A \\ B";`, the value of "a1" is now "A \ B". The previous implementation was importing the value "A \\ B". -** Fix error when importing SysML snippets referencing unknown units which could make the resluting model inconsistent +** Fix error when importing SysML snippets referencing unknown units which could make the resulting model inconsistent * In _Explorer_ view: