diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 3b5cef437..c34649654 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -84,6 +84,7 @@ The cache holding standard libraries can be invalidated for a specific test meth - https://github.com/eclipse-syson/syson/issues/2152[#2152] [diagrams] Leverage the latest selection dialog changes to allow creating a sub action with and without associating the sub action with another `ActionUsage`. - https://github.com/eclipse-syson/syson/issues/2161[#2161] [diagrams] Leverage the latest selection dialog changes to optionally allow creating a `SatisfyRequirement` graphical node, either standalone, feature typed, or subsetting a `RequirementUsage` by reference. Also use that same tool in the `interconnection` compartment. +- https://github.com/eclipse-syson/syson/issues/2172[#2172] [diagrams] Add keybinding to the _Duplicate Element_ tool (ctrl+d). === New features diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVDuplicateNodeTest.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVDuplicateNodeTest.java index 569edbba6..d1c7423ed 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVDuplicateNodeTest.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVDuplicateNodeTest.java @@ -31,6 +31,7 @@ import org.eclipse.sirius.components.diagrams.OutsideLabel; import org.eclipse.sirius.components.diagrams.ViewModifier; import org.eclipse.sirius.components.diagrams.tests.navigation.DiagramNavigator; +import org.eclipse.sirius.components.view.diagram.NodeTool; import org.eclipse.sirius.components.view.emf.diagram.IDiagramIdProvider; import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; import org.eclipse.syson.AbstractIntegrationTests; @@ -52,6 +53,7 @@ import org.eclipse.syson.sysml.PartUsage; import org.eclipse.syson.sysml.StateDefinition; import org.eclipse.syson.sysml.SysmlPackage; +import org.eclipse.syson.sysml.helper.EMFUtils; import org.eclipse.syson.sysml.helper.LabelConstants; import org.eclipse.syson.util.IDescriptionNameGenerator; import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers; @@ -60,6 +62,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.transaction.TestTransaction; import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Flux; @@ -132,8 +135,8 @@ public void setUp() { } @DisplayName("GIVEN a General View diagram, WHEN duplicating a Part Usage node with attributes, THEN the semantic element is duplicated with its content and its representation") - @Test @GivenSysONServer({ GeneralViewItemAndAttributeProjectData.SCRIPT_PATH }) + @Test public void checkTopUsageNodeDuplication() { var flux = this.givenSubscriptionToDiagram(); @@ -189,9 +192,32 @@ public void checkTopUsageNodeDuplication() { .verify(Duration.ofSeconds(10)); } - @DisplayName("GIVEN a General View diagram, WHEN duplicating an ItemUsage bordered node, THEN the semantic element is duplicated with its representation") + @DisplayName("GIVEN a General View diagram, WHEN inspecting duplicate tools, THEN they provide the Ctrl+D keybinding") + @GivenSysONServer({ GeneralViewItemAndAttributeProjectData.SCRIPT_PATH }) @Test + public void checkDuplicateToolsKeyBinding() { + TestTransaction.flagForCommit(); + TestTransaction.end(); + + var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GeneralViewItemAndAttributeProjectData.EDITING_CONTEXT_ID, + SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID); + + var duplicateElementTool = EMFUtils.allContainedObjectOfType(diagramDescription, NodeTool.class) + .filter(nodeTool -> "Duplicate Element".equals(nodeTool.getName())) + .findFirst(); + assertThat(duplicateElementTool).isPresent(); + this.assertCtrlDKeyBinding(duplicateElementTool.get()); + + var duplicateElementsGroupTool = EMFUtils.allContainedObjectOfType(diagramDescription.getGroupPalette(), NodeTool.class) + .filter(nodeTool -> "Duplicate Elements".equals(nodeTool.getName())) + .findFirst(); + assertThat(duplicateElementsGroupTool).isPresent(); + this.assertCtrlDKeyBinding(duplicateElementsGroupTool.get()); + } + + @DisplayName("GIVEN a General View diagram, WHEN duplicating an ItemUsage bordered node, THEN the semantic element is duplicated with its representation") @GivenSysONServer({ GeneralViewItemAndAttributeProjectData.SCRIPT_PATH }) + @Test public void checkBorderedNodeUsageNodeDuplication() { var flux = this.givenSubscriptionToDiagram(); @@ -325,8 +351,8 @@ public void checkCompartmentItemUsageNodeDuplication() { } @DisplayName("GIVEN a General View diagram, WHEN duplicating two top container nodes, THEN both semantic elements and representations are duplicated") - @Test @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) + @Test public void checkMultiSelectionContainerNodeDuplication() { var flux = this.givenSubscriptionToDiagram(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, GeneralViewWithTopNodesTestProjectData.GraphicalIds.DIAGRAM_ID); var topNodesSemanticCheckerService = new SemanticCheckerService(this.semanticRunnableFactory, this.objectSearchService, GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, @@ -387,8 +413,8 @@ public void checkMultiSelectionContainerNodeDuplication() { } @DisplayName("GIVEN a General View diagram, WHEN a part is created in a Package and duplicated with a StateDefinition, THEN both semantic elements and representations are duplicated") - @Test @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) + @Test public void checkMultiSelectionDifferentContainerNodeDuplication() { var flux = this.givenSubscriptionToDiagram(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, GeneralViewWithTopNodesTestProjectData.GraphicalIds.DIAGRAM_ID); var topNodesSemanticCheckerService = new SemanticCheckerService(this.semanticRunnableFactory, this.objectSearchService, GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, @@ -499,4 +525,15 @@ private boolean containsLabel(Node node, String label) { return match; } + private void assertCtrlDKeyBinding(NodeTool nodeTool) { + assertThat(nodeTool.getKeyBindings()) + .singleElement() + .satisfies(keyBinding -> { + assertThat(keyBinding.isCtrl()).isTrue(); + assertThat(keyBinding.isAlt()).isFalse(); + assertThat(keyBinding.isMeta()).isFalse(); + assertThat(keyBinding.getKey()).isEqualTo("d"); + }); + } + } diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractNodeDescriptionProvider.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractNodeDescriptionProvider.java index 8a42616a2..db73ad98f 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractNodeDescriptionProvider.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/nodes/AbstractNodeDescriptionProvider.java @@ -100,6 +100,7 @@ protected NodeTool getDuplicateElementAndNodeTool() { return this.diagramBuilderHelper.newNodeTool() .name("Duplicate Element") .iconURLsExpression("/images/content_copy.svg") + .keyBindings(this.viewBuilderHelper.newKeyBinding().ctrl(true).key("d").build()) .preconditionExpression(AQLConstants.AQL + "self.oclIsKindOf(sysml::Element) and not self.oclIsKindOf(sysml::Relationship)") .body(this.viewBuilderHelper.newChangeContext() .expression( @@ -117,6 +118,7 @@ protected NodeTool getDuplicateElementsAndNodesTool() { return this.diagramBuilderHelper.newNodeTool() .name("Duplicate Element") .iconURLsExpression("/images/content_copy.svg") + .keyBindings(this.viewBuilderHelper.newKeyBinding().ctrl(true).key("d").build()) .preconditionExpression(AQLConstants.AQL + "selectedNodes->notEmpty() and selectedEdges->isEmpty() and self->forAll(e | e.oclIsKindOf(sysml::Element) and not e.oclIsKindOf(sysml::Relationship))") .body(this.viewBuilderHelper.newChangeContext() diff --git a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionProvider.java b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionProvider.java index 774ee55c6..ded90d231 100644 --- a/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionProvider.java +++ b/backend/views/syson-standard-diagrams-view/src/main/java/org/eclipse/syson/standard/diagrams/view/SDVDiagramDescriptionProvider.java @@ -417,6 +417,7 @@ private NodeTool createDuplicateElementsGroupTool() { return this.diagramBuilderHelper.newNodeTool() .name("Duplicate Elements") .iconURLsExpression("/images/content_copy.svg") + .keyBindings(this.viewBuilderHelper.newKeyBinding().ctrl(true).key("d").build()) .preconditionExpression("aql:selectedNodes->notEmpty() and selectedEdges->isEmpty() and self->forAll(e | e.oclIsKindOf(sysml::Element) and not e.oclIsKindOf(sysml::Relationship))") .body(this.viewBuilderHelper.newChangeContext() .expression(ServiceMethod.of4(DiagramMutationAQLService::duplicateElementAndExpose) diff --git a/doc/content/modules/user-manual/pages/features/keyboard-shortcuts.adoc b/doc/content/modules/user-manual/pages/features/keyboard-shortcuts.adoc index 09a5b3498..daad98016 100644 --- a/doc/content/modules/user-manual/pages/features/keyboard-shortcuts.adoc +++ b/doc/content/modules/user-manual/pages/features/keyboard-shortcuts.adoc @@ -21,6 +21,10 @@ User can press `F2` key or start typing the new value directly to efficiently up Explore further by referring to the following how-tos for xref:user-manual:features/general-view.adoc#edit-label[Edit Label action]. +== Duplicate + +User can press `Ctrl+d` to duplicate the selected element from the representation editor. + == Multi-selection Two ways to select several elements exist: @@ -39,4 +43,4 @@ After releasing the click, all element in the area are selected. User can display the filter bar by holding `Ctrl+f` (Windows/Linux) or `Cmd+f` (MacOS) from the {explorer} view. -Explore further by referring to the following how-tos for xref:hands-on/how-tos/explorer.adoc#explorer-filter[Filter elements]. \ No newline at end of file +Explore further by referring to the following how-tos for xref:hands-on/how-tos/explorer.adoc#explorer-filter[Filter elements]. diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc index 3fb3c0a62..35d8f52f6 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.5.0.adoc @@ -38,7 +38,7 @@ image::release-notes-stakeholder-node.png[Default representation of Stakeholder ** Improve the tool to create a `FlowUsage` from a `ConnectionUsage` by making the payload selection optional. ** Merge the two objective creation tools into a single tool by leveraging the updated selection dialog which make the specialization selection optional. ** Merge the two perform action creation tools into a single tool by leveraging the updated selection dialog which make the selection of a referenced action optional. -** Improve the _Duplicate Element_ tool to support multi-selection in standard diagrams. +** Improve the _Duplicate Element_ tool to support multi-selection in standard diagrams, and also associate it with a keybinding (_ctrl+d_). ** Improve the _View As_ tool on graphical nodes to support multi-selection in standard diagrams. ** Improve the _Add existing elements_ tool on graphical nodes to support multi-selection in standard diagrams. ** Improve the tools to create `PerformActionUsage` by making the selection of a subsetted reference optional. diff --git a/doc/content/modules/user-manual/partials/manage-elements-diagram.adoc b/doc/content/modules/user-manual/partials/manage-elements-diagram.adoc index a51b48187..369215a45 100644 --- a/doc/content/modules/user-manual/partials/manage-elements-diagram.adoc +++ b/doc/content/modules/user-manual/partials/manage-elements-diagram.adoc @@ -57,5 +57,6 @@ You can reset it by deleting the {product} data in cache of your browser. === Duplicate Element User can duplicate an `Element` (and its representation node) by clicking on the _Duplicate Element_ button in the palette. +User can also duplicate the selected `Element` with the `Ctrl+d` keyboard shortcut from the representation editor. -image::manage-elements-duplicate-from-diagram.png[Duplicate element from Diagram] \ No newline at end of file +image::manage-elements-duplicate-from-diagram.png[Duplicate element from Diagram]