diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index b211e0e7b4..4ee1197e40 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -73,6 +73,7 @@ This removes Rollup warnings about missing global names for externalized peer de - [releng] Reduce the log of the frontend tasks `format-lint` and `build-dev` by default to make tasks such as `build` or `start` less verbose. - [cleanup] Use only one border node description provider for all item border nodes. - https://github.com/eclipse-syson/syson/issues/2452[#2352] [diagram] Decouple SysON tools from Sirius Web's DefaultToolsFactory: SysON now defines its own `DiagramDefaultToolsFactory` providing show/hide tools. +- https://github.com/eclipse-syson/syson/issues/2453[#2453] [diagrams] Add the Show/Hide section and associated tools to the group palette. === New features diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVGroupPaletteShowHideToolsTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVGroupPaletteShowHideToolsTests.java new file mode 100644 index 0000000000..87f4f60861 --- /dev/null +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVGroupPaletteShowHideToolsTests.java @@ -0,0 +1,335 @@ +/******************************************************************************* + * Copyright (c) 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 + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.syson.application.controllers.diagrams.general.view; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.eclipse.sirius.components.diagrams.tests.DiagramEventPayloadConsumer.assertRefreshedDiagramThat; + +import com.jayway.jsonpath.JsonPath; + +import java.time.Duration; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +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.HideDiagramElementInput; +import org.eclipse.sirius.components.core.api.SuccessPayload; +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.HideDiagramElementMutationRunner; +import org.eclipse.sirius.components.diagrams.tests.graphql.PaletteQueryRunner; +import org.eclipse.sirius.components.diagrams.tests.navigation.DiagramNavigator; +import org.eclipse.sirius.components.view.emf.diagram.IDiagramIdProvider; +import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState; +import org.eclipse.syson.AbstractIntegrationTests; +import org.eclipse.syson.application.controllers.diagrams.testers.ToolTester; +import org.eclipse.syson.application.data.GVSimpleNestedAndTreeElementsTestProjectData; +import org.eclipse.syson.services.diagrams.DiagramDescriptionIdProvider; +import org.eclipse.syson.services.diagrams.api.IGivenDiagramDescription; +import org.eclipse.syson.services.diagrams.api.IGivenDiagramSubscription; +import org.eclipse.syson.tests.api.GivenSysONServer; +import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.transaction.annotation.Transactional; + +import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; + +/** + * Tests the tools of the "Show/Hide" section of the group palette, in the General View diagram. + * + * @author tgiraudet + */ +@Transactional +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class GVGroupPaletteShowHideToolsTests extends AbstractIntegrationTests { + + private static final String PART_A_ID = GVSimpleNestedAndTreeElementsTestProjectData.GraphicalIds.PART_A_ID; + + private static final String PARTDEF_A_ID = GVSimpleNestedAndTreeElementsTestProjectData.GraphicalIds.PARTDEF_A_ID; + + private static final String ACTION_A_ID = GVSimpleNestedAndTreeElementsTestProjectData.GraphicalIds.ACTION_A_ID; + + @Autowired + private IGivenInitialServerState givenInitialServerState; + + @Autowired + private IGivenDiagramSubscription givenDiagramSubscription; + + @Autowired + private IGivenDiagramDescription givenDiagramDescription; + + @Autowired + private IDiagramIdProvider diagramIdProvider; + + @Autowired + private ToolTester toolTester; + + @Autowired + private PaletteQueryRunner paletteQueryRunner; + + @Autowired + private HideDiagramElementMutationRunner hideDiagramElementMutationRunner; + + private Flux givenSubscriptionToDiagram() { + var diagramEventInput = new DiagramEventInput(UUID.randomUUID(), + GVSimpleNestedAndTreeElementsTestProjectData.EDITING_CONTEXT_ID, + GVSimpleNestedAndTreeElementsTestProjectData.GraphicalIds.DIAGRAM_ID); + return this.givenDiagramSubscription.subscribe(diagramEventInput); + } + + @BeforeEach + public void setUp() { + this.givenInitialServerState.initialize(); + } + + @DisplayName("GIVEN a GV diagram, WHEN the group palette is requested on a multi-selection of nodes with content, THEN it contains a 'Show/Hide' section with all the show/hide tools") + @GivenSysONServer({ GVSimpleNestedAndTreeElementsTestProjectData.SCRIPT_PATH }) + @Test + public void groupPaletteContainsShowHideSection() { + var flux = this.givenSubscriptionToDiagram(); + + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> assertThat(this.compartmentsOf(diagram, PART_A_ID)).isNotEmpty()); + + Runnable getGroupPalette = () -> { + Map variables = Map.of( + "editingContextId", GVSimpleNestedAndTreeElementsTestProjectData.EDITING_CONTEXT_ID, + "representationId", GVSimpleNestedAndTreeElementsTestProjectData.GraphicalIds.DIAGRAM_ID, + "diagramElementIds", List.of(PART_A_ID, PARTDEF_A_ID)); + var result = this.paletteQueryRunner.run(variables); + var toolLabels = JsonPath.>read(result.data(), "$.data.viewer.editingContext.representation.description.palette.paletteEntries[?(@.label == 'Show/Hide')].tools[*].label"); + assertThat(toolLabels).containsExactly("Hide", "Hide all content", "Show all content", "Reset content", "Show valued content"); + }; + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(getGroupPalette) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN a GV diagram with several visible nodes, WHEN the group 'Hide' tool is applied on a multi-selection, THEN all the selected nodes are hidden") + @GivenSysONServer({ GVSimpleNestedAndTreeElementsTestProjectData.SCRIPT_PATH }) + @Test + public void invokeHideToolOnMultiSelection() { + var flux = this.givenSubscriptionToDiagram(); + var hideToolId = this.getGroupToolId("Hide"); + + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> { + assertThat(this.nodeWithId(diagram, PART_A_ID).getState()).isEqualTo(ViewModifier.Normal); + assertThat(this.nodeWithId(diagram, PARTDEF_A_ID).getState()).isEqualTo(ViewModifier.Normal); + }); + + Runnable hideTool = () -> this.invokeGroupTool(hideToolId, List.of(PART_A_ID, PARTDEF_A_ID)); + + Consumer updatedDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> { + assertThat(this.nodeWithId(diagram, PART_A_ID).getState()).isEqualTo(ViewModifier.Hidden); + assertThat(this.nodeWithId(diagram, PARTDEF_A_ID).getState()).isEqualTo(ViewModifier.Hidden); + assertThat(this.nodeWithId(diagram, ACTION_A_ID).getState()) + .as("The nodes that are not part of the selection should not be impacted") + .isEqualTo(ViewModifier.Normal); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(hideTool) + .consumeNextWith(updatedDiagramContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN a GV diagram with several nodes with a hidden content, WHEN the group 'Show all content' tool is applied on a multi-selection, THEN the content of all the selected nodes is revealed") + @GivenSysONServer({ GVSimpleNestedAndTreeElementsTestProjectData.SCRIPT_PATH }) + @Test + public void invokeShowAllContentToolOnMultiSelection() { + var flux = this.givenSubscriptionToDiagram(); + var showAllContentToolId = this.getGroupToolId("Show all content"); + + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> { + assertThat(this.compartmentsOf(diagram, PART_A_ID)).isNotEmpty().allMatch(this::isHidden); + assertThat(this.compartmentsOf(diagram, PARTDEF_A_ID)).isNotEmpty().allMatch(this::isHidden); + }); + + Runnable showAllContentTool = () -> this.invokeGroupTool(showAllContentToolId, List.of(PART_A_ID, PARTDEF_A_ID)); + + Consumer updatedDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> { + assertThat(this.compartmentsOf(diagram, PART_A_ID)).isNotEmpty().noneMatch(this::isHidden); + assertThat(this.compartmentsOf(diagram, PARTDEF_A_ID)).isNotEmpty().noneMatch(this::isHidden); + assertThat(this.compartmentsOf(diagram, ACTION_A_ID)) + .as("The content of the nodes that are not part of the selection should not be impacted") + .isNotEmpty() + .allMatch(this::isHidden); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(showAllContentTool) + .consumeNextWith(updatedDiagramContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN a GV diagram with several nodes with a visible content, WHEN the group 'Hide all content' tool is applied on a multi-selection, THEN the content of all the selected nodes is hidden") + @GivenSysONServer({ GVSimpleNestedAndTreeElementsTestProjectData.SCRIPT_PATH }) + @Test + public void invokeHideAllContentToolOnMultiSelection() { + var flux = this.givenSubscriptionToDiagram(); + var hideAllContentToolId = this.getGroupToolId("Hide all content"); + + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> { + var payloadTypename = this.revealCompartments(diagram, PART_A_ID, PARTDEF_A_ID); + assertThat(payloadTypename).isEqualTo(SuccessPayload.class.getSimpleName()); + }); + + Consumer diagramContentConsumerAfterReveal = assertRefreshedDiagramThat(diagram -> { + assertThat(this.compartmentsOf(diagram, PART_A_ID)).isNotEmpty().noneMatch(this::isHidden); + assertThat(this.compartmentsOf(diagram, PARTDEF_A_ID)).isNotEmpty().noneMatch(this::isHidden); + }); + + Runnable hideAllContentTool = () -> this.invokeGroupTool(hideAllContentToolId, List.of(PART_A_ID, PARTDEF_A_ID)); + + Consumer updatedDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> { + assertThat(this.compartmentsOf(diagram, PART_A_ID)).isNotEmpty().allMatch(this::isHidden); + assertThat(this.compartmentsOf(diagram, PARTDEF_A_ID)).isNotEmpty().allMatch(this::isHidden); + assertThat(this.nodeWithId(diagram, PART_A_ID).getState()) + .as("The selected nodes themselves should remain visible") + .isEqualTo(ViewModifier.Normal); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .consumeNextWith(diagramContentConsumerAfterReveal) + .then(hideAllContentTool) + .consumeNextWith(updatedDiagramContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN a GV diagram with several nodes with a visible content, WHEN the group 'Reset content' tool is applied on a multi-selection, THEN the content of all the selected nodes recovers its default visibility") + @GivenSysONServer({ GVSimpleNestedAndTreeElementsTestProjectData.SCRIPT_PATH }) + @Test + public void invokeResetContentToolOnMultiSelection() { + var flux = this.givenSubscriptionToDiagram(); + var resetContentToolId = this.getGroupToolId("Reset content"); + + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> { + var payloadTypename = this.revealCompartments(diagram, PART_A_ID, PARTDEF_A_ID); + assertThat(payloadTypename).isEqualTo(SuccessPayload.class.getSimpleName()); + }); + + Consumer diagramContentConsumerAfterReveal = assertRefreshedDiagramThat(diagram -> { + assertThat(this.compartmentsOf(diagram, PART_A_ID)).isNotEmpty().noneMatch(this::isHidden); + assertThat(this.compartmentsOf(diagram, PARTDEF_A_ID)).isNotEmpty().noneMatch(this::isHidden); + }); + + Runnable resetContentTool = () -> this.invokeGroupTool(resetContentToolId, List.of(PART_A_ID, PARTDEF_A_ID)); + + Consumer updatedDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> { + assertThat(this.compartmentsOf(diagram, PART_A_ID)) + .as("Compartments are hidden by default, they should be hidden again once their visibility modifiers are reset") + .isNotEmpty() + .allMatch(this::isHidden); + assertThat(this.compartmentsOf(diagram, PARTDEF_A_ID)).isNotEmpty().allMatch(this::isHidden); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .consumeNextWith(diagramContentConsumerAfterReveal) + .then(resetContentTool) + .consumeNextWith(updatedDiagramContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + @DisplayName("GIVEN a GV diagram with several nodes with a hidden content, WHEN the group 'Show valued content' tool is applied on a multi-selection, THEN only the content that is not empty is revealed") + @GivenSysONServer({ GVSimpleNestedAndTreeElementsTestProjectData.SCRIPT_PATH }) + @Test + public void invokeShowValuedContentToolOnMultiSelection() { + var flux = this.givenSubscriptionToDiagram(); + var showValuedContentToolId = this.getGroupToolId("Show valued content"); + + Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> { + assertThat(this.compartmentsOf(diagram, ACTION_A_ID)).isNotEmpty().allMatch(this::isHidden); + assertThat(this.compartmentsOf(diagram, PART_A_ID)).isNotEmpty().allMatch(this::isHidden); + }); + + Runnable showValuedContentTool = () -> this.invokeGroupTool(showValuedContentToolId, List.of(ACTION_A_ID, PART_A_ID)); + + Consumer updatedDiagramContentConsumer = assertRefreshedDiagramThat(diagram -> { + assertThat(this.compartmentsOf(diagram, ACTION_A_ID)) + .filteredOn(compartment -> !this.isHidden(compartment)) + .extracting(compartment -> compartment.getInsideLabel().getText()) + .as("Only the compartments of actionA containing something should be revealed") + .containsExactlyInAnyOrder("actions", "action flow"); + assertThat(this.compartmentsOf(diagram, PART_A_ID)) + .as("partA has no content, none of its compartments should be revealed") + .isNotEmpty() + .allMatch(this::isHidden); + }); + + StepVerifier.create(flux) + .consumeNextWith(initialDiagramContentConsumer) + .then(showValuedContentTool) + .consumeNextWith(updatedDiagramContentConsumer) + .thenCancel() + .verify(Duration.ofSeconds(10)); + } + + private String getGroupToolId(String toolName) { + var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GVSimpleNestedAndTreeElementsTestProjectData.EDITING_CONTEXT_ID, + SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID); + return new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider).getGroupNodeToolId(toolName); + } + + private void invokeGroupTool(String toolId, List diagramElementIds) { + this.toolTester.invokeTool(GVSimpleNestedAndTreeElementsTestProjectData.EDITING_CONTEXT_ID, GVSimpleNestedAndTreeElementsTestProjectData.GraphicalIds.DIAGRAM_ID, diagramElementIds, toolId, + List.of()); + } + + /** + * Reveals the compartments of the given nodes, and returns the type name of the payload of the mutation. + */ + private String revealCompartments(Diagram diagram, String... nodeIds) { + Set compartmentIds = Stream.of(nodeIds) + .flatMap(nodeId -> this.compartmentsOf(diagram, nodeId).stream()) + .map(Node::getId) + .collect(Collectors.toSet()); + var input = new HideDiagramElementInput(UUID.randomUUID(), GVSimpleNestedAndTreeElementsTestProjectData.EDITING_CONTEXT_ID, + GVSimpleNestedAndTreeElementsTestProjectData.GraphicalIds.DIAGRAM_ID, compartmentIds, false); + var result = this.hideDiagramElementMutationRunner.run(input); + return JsonPath.read(result.data(), "$.data.hideDiagramElement.__typename"); + } + + private Node nodeWithId(Diagram diagram, String nodeId) { + return new DiagramNavigator(diagram).nodeWithId(nodeId).getNode(); + } + + private List compartmentsOf(Diagram diagram, String nodeId) { + return this.nodeWithId(diagram, nodeId).getChildNodes(); + } + + private boolean isHidden(Node node) { + return node.getModifiers().contains(ViewModifier.Hidden); + } +} diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/DiagramDefaultGroupToolsFactory.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/DiagramDefaultGroupToolsFactory.java new file mode 100644 index 0000000000..90cfca8844 --- /dev/null +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/DiagramDefaultGroupToolsFactory.java @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 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 + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Obeo - initial API and implementation + *******************************************************************************/ + +package org.eclipse.syson.diagram.common.view; + +import org.eclipse.sirius.components.view.ChangeContext; +import org.eclipse.sirius.components.view.ViewFactory; +import org.eclipse.sirius.components.view.diagram.DiagramFactory; +import org.eclipse.sirius.components.view.diagram.NodeTool; +import org.eclipse.sirius.components.view.diagram.NodeToolSection; + +/** + * Factory to create generic group tool descriptions which invoke the default/canonical behaviors. + * + * @author tgiraudet + */ +public class DiagramDefaultGroupToolsFactory { + + private static final String HAS_CHILDREN_EXPRESSION = "aql:selectedNodes.getChildNodes()->notEmpty() or selectedNodes.getBorderNodes()->notEmpty()"; + + private static final String HAS_HIDDEN_CHILDREN_EXPRESSION = "aql:selectedNodes.getChildNodes()->union(selectedNodes.getBorderNodes())->select(n | n.isHidden())->notEmpty()"; + + public NodeToolSection createDefaultHideRevealNodeToolSection() { + NodeToolSection nodeToolSection = DiagramFactory.eINSTANCE.createNodeToolSection(); + nodeToolSection.setName("Show/Hide"); + nodeToolSection.getNodeTools().add(this.createDefaultHideNodeTool()); + nodeToolSection.getNodeTools().add(this.createDefaultHideAllChildrenNodeTool()); + nodeToolSection.getNodeTools().add(this.createDefaultRevealAllChildrenNodeTool()); + nodeToolSection.getNodeTools().add(this.createDefaultResetAllChildrenVisibilityModifiersNodeTool()); + nodeToolSection.getNodeTools().add(this.createDefaultRevealChildrenWithValueNodeTool()); + return nodeToolSection; + } + + public NodeTool createDefaultHideNodeTool() { + NodeTool newNodeTool = DiagramFactory.eINSTANCE.createNodeTool(); + newNodeTool.setName("Hide"); + ChangeContext body = ViewFactory.eINSTANCE.createChangeContext(); + body.setExpression("aql:diagramServices.hide(selectedNodes)"); + newNodeTool.getBody().add(body); + newNodeTool.setIconURLsExpression("aql:'/icons/full/obj16/HideTool.svg'"); + return newNodeTool; + } + + public NodeTool createDefaultHideAllChildrenNodeTool() { + NodeTool newNodeTool = DiagramFactory.eINSTANCE.createNodeTool(); + newNodeTool.setName("Hide all content"); + ChangeContext body = ViewFactory.eINSTANCE.createChangeContext(); + body.setExpression("aql:diagramServices.hide(selectedNodes.getChildNodes()->union(selectedNodes.getBorderNodes()))"); + newNodeTool.getBody().add(body); + newNodeTool.setIconURLsExpression("aql:'/icons/full/obj16/HideTool.svg'"); + newNodeTool.setPreconditionExpression("aql:selectedNodes.getChildNodes()->notEmpty() or selectedNodes.getBorderNodes()->notEmpty()"); + return newNodeTool; + } + + public NodeTool createDefaultRevealAllChildrenNodeTool() { + NodeTool newNodeTool = DiagramFactory.eINSTANCE.createNodeTool(); + newNodeTool.setName("Show all content"); + ChangeContext body = ViewFactory.eINSTANCE.createChangeContext(); + body.setExpression("aql:diagramServices.reveal(selectedNodes.getChildNodes()->union(selectedNodes.getBorderNodes()))"); + newNodeTool.getBody().add(body); + newNodeTool.setIconURLsExpression("aql:'/icons/full/obj16/ShowTool.svg'"); + newNodeTool.setPreconditionExpression("aql:selectedNodes.getChildNodes()->union(selectedNodes.getBorderNodes())->select(n | n.isHidden())->notEmpty()"); + return newNodeTool; + } + + public NodeTool createDefaultResetAllChildrenVisibilityModifiersNodeTool() { + NodeTool newNodeTool = DiagramFactory.eINSTANCE.createNodeTool(); + newNodeTool.setName("Reset content"); + ChangeContext body = ViewFactory.eINSTANCE.createChangeContext(); + body.setExpression("aql:diagramServices.resetViewModifiers(selectedNodes.getChildNodes()->union(selectedNodes.getBorderNodes()))"); + newNodeTool.getBody().add(body); + newNodeTool.setIconURLsExpression("aql:'/icons/full/obj16/ShowTool.svg'"); + newNodeTool.setPreconditionExpression(HAS_CHILDREN_EXPRESSION); + return newNodeTool; + } + + public NodeTool createDefaultRevealChildrenWithValueNodeTool() { + NodeTool newNodeTool = DiagramFactory.eINSTANCE.createNodeTool(); + newNodeTool.setName("Show valued content"); + ChangeContext body = ViewFactory.eINSTANCE.createChangeContext(); + body.setExpression("aql:diagramServices.reveal(selectedNodes.getChildNodes()->union(selectedNodes.getBorderNodes())->select(n | n.getChildNodes()->notEmpty() or n.getBorderNodes()->notEmpty()))"); + newNodeTool.getBody().add(body); + newNodeTool.setIconURLsExpression("aql:'/icons/full/obj16/ShowTool.svg'"); + newNodeTool.setPreconditionExpression(HAS_HIDDEN_CHILDREN_EXPRESSION); + return newNodeTool; + } +} 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 3a5568bafd..dc6ae87f68 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 @@ -42,6 +42,7 @@ import org.eclipse.sirius.components.view.diagram.NodeTool; import org.eclipse.sirius.components.view.diagram.ToolSection; import org.eclipse.sirius.components.view.emf.diagram.ViewDiagramDescriptionConverter; +import org.eclipse.syson.diagram.common.view.DiagramDefaultGroupToolsFactory; import org.eclipse.syson.diagram.common.view.ViewDiagramElementFinder; import org.eclipse.syson.diagram.common.view.edges.AnnotationEdgeDescriptionProvider; import org.eclipse.syson.diagram.common.view.nodes.ActionFlowCompartmentNodeDescriptionProvider; @@ -431,6 +432,7 @@ private List createGroupToolSections() { .build(); groupToolSections.add(viewAsToolSection); groupToolSections.add(this.toolDescriptionService.relatedElementsGroupToolSection()); + groupToolSections.add(new DiagramDefaultGroupToolsFactory().createDefaultHideRevealNodeToolSection()); return groupToolSections; } diff --git a/doc/content/modules/user-manual/assets/images/manage-group-element-toolbar.png b/doc/content/modules/user-manual/assets/images/manage-group-element-toolbar.png index 1449dcacac..6fa6e2a6a8 100644 Binary files a/doc/content/modules/user-manual/assets/images/manage-group-element-toolbar.png and b/doc/content/modules/user-manual/assets/images/manage-group-element-toolbar.png differ diff --git a/doc/content/modules/user-manual/assets/images/release-notes-show-hide-section-group-palette.png b/doc/content/modules/user-manual/assets/images/release-notes-show-hide-section-group-palette.png new file mode 100644 index 0000000000..80831ef1e5 Binary files /dev/null and b/doc/content/modules/user-manual/assets/images/release-notes-show-hide-section-group-palette.png differ diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc index fc685fb454..17e07ad456 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.9.0.adoc @@ -149,6 +149,11 @@ image::release-notes-hierarchical-requirements-table-view.png[Hierarchical requi + image::release-notes-hierarchical-requirements-table-view-create-nested-requirement.png[New sub-requirement in row action menu, width=90%,height=90%] +** When selecting multiple graphical nodes, the group palette now displays the _Show/Hide_ section just like the single selection palette. +The associated tools are the same ones: _Hide_; _Hide all content_; _Show all content_; _Reset content_; _Show valued content_. ++ +image::release-notes-show-hide-section-group-palette.png[New Show/Hide section in group palette, width=90%,height=90%] + == Technical details * For technical details on this {product} release, including breaking changes, please refer to the https://github.com/eclipse-syson/syson/blob/main/CHANGELOG.adoc[changelog]. diff --git a/doc/content/modules/user-manual/partials/manage-group-elements.adoc b/doc/content/modules/user-manual/partials/manage-group-elements.adoc index 3288ceddd5..1fc19b4132 100644 --- a/doc/content/modules/user-manual/partials/manage-group-elements.adoc +++ b/doc/content/modules/user-manual/partials/manage-group-elements.adoc @@ -11,10 +11,12 @@ image::manage-group-element-toolbar.png[Group element toolbar] User can still used basic tools such as : * Hide elements, +* Hide all their content, +* Show their valued content, * Fade elements, * Pin elements. -A new tool section appears in this toolbar. +A layout tool section appears in this toolbar. Many tools acting on selected elements layout can be found in this tool section : * Align left,