Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ A key binding needs to contain at least one of `Control`, `Alt`, or `Meta` to be
- https://github.com/eclipse-sirius/sirius-web/issues/5982[#5982] [tree] Add the ability to trigger context menu entries using key bindings.
As for diagram tools, it is now possible to add `KeyBinding` instances to context menu entries in trees.
Note that for the moment, only entries that aren't overridden in the frontend can be triggered with key bindings, although it is possible to define key bindings for any entry.
- https://github.com/eclipse-sirius/sirius-web/issues/6118[#6118] [diagram] Add support for auto layout until change


=== Improvements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,20 @@ test.describe('diagram - arrange all', () => {

test('when a arrange all is triggered, then a fit to screen is applied', async ({ page }) => {
await expect(page.getByTestId('rf__wrapper')).toBeAttached();
await expect(page.getByTestId('FreeForm - Wifi')).toBeInViewport();
const wifiNode = new PlaywrightNode(page, 'Wifi');
await expect(wifiNode.nodeLocator).toBeInViewport();
//move view port to hide some nodes
await page.getByTestId('rf__wrapper').hover({ position: { x: 10, y: 10 } });
await page.mouse.down();
await page.mouse.move(10, 10, { steps: 2 });
await page.mouse.up();

await expect(page.getByTestId('FreeForm - Wifi')).not.toBeInViewport();
await expect(wifiNode.nodeLocator).not.toBeInViewport();

await page.getByTestId('arrange-all-menu').click();
await page.getByTestId('arrange-all-elk-layered').click();

await expect(page.getByTestId('FreeForm - Wifi')).toBeInViewport();
await expect(wifiNode.nodeLocator).toBeInViewport();
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,40 @@ test.describe('diagram - auto-layout', () => {
);
});
});

test.describe('diagram - auto-until-manual', () => {
let projectId;
test.beforeEach(async ({ page, request }) => {
const project = await new PlaywrightProject(request).createProject('Studio', 'studio-template');
projectId = project.projectId;

await page.goto(`/projects/${projectId}/edit/`);
});

test.afterEach(async ({ request }) => {
await new PlaywrightProject(request).deleteProject(projectId);
});

test('when a diagram with auto-until-manual is opened, then all nodes are positioned according to the elk layout', async ({
page,
}) => {
await expect(page.getByTestId('rf__wrapper')).toBeAttached();

const node = new PlaywrightNode(page, 'Entity1', 'List');
const position = await node.getReactFlowXYPosition();
expect(position.x).toBe(12);
expect(position.y).toBe(242);
});

test('when moving a node on an auto-until-manual diagram, then move is not reset', async ({ page }) => {
await expect(page.getByTestId('rf__wrapper')).toBeAttached();

const rootNode = new PlaywrightNode(page, 'Entity1', 'List');
const rootPositionBefore = await rootNode.getDOMXYPosition();
await rootNode.move({ x: 100, y: 100 });

const rootPositionAfter = await rootNode.getDOMXYPosition();
expect(rootPositionAfter.x).not.toBe(rootPositionBefore.x);
expect(rootPositionAfter.y).not.toBe(rootPositionBefore.y);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ test.describe('edge on edge', () => {
await page.mouse.up();
await expect(edges).toHaveCount(2);
//Create node to edge
playwrightNode2b.click();
await playwrightNode2b.click();
await page.getByTestId('creationhandle-bottom').hover();
await page.mouse.down();
const playwrightEdge = new PlaywrightEdge(page);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,6 @@ import { PlaywrightProject } from '../../helpers/PlaywrightProject';
test.describe('diagram - key bindings', () => {
let projectId;
test.beforeEach(async ({ page, request }) => {
await page.addInitScript(() => {
// @ts-expect-error: we use a variable in the DOM to disable `fitView` functionality for Cypress tests.
window.document.DEACTIVATE_FIT_VIEW_FOR_CYPRESS_TESTS = true;
});
const project = await new PlaywrightProject(request).createProject('Studio', 'blank-studio-template');
projectId = project.projectId;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
import { expect, test } from '@playwright/test';
import { expect, test, Page } from '@playwright/test';
import { PlaywrightExplorer } from '../../helpers/PlaywrightExplorer';
import { PlaywrightNode } from '../../helpers/PlaywrightNode';
import { PlaywrightProject } from '../../helpers/PlaywrightProject';
Expand Down Expand Up @@ -87,12 +87,17 @@ test.describe('diagram - drag and drop of multiple elements', () => {
// Now, inside the diagram, select both attributes from the 'Source' entity
const attr1NodeBefore = new PlaywrightNode(page, 'attr1', 'IconLabel');
await attr1NodeBefore.click();
// Hide Node Panel Info to avoid overlap in diagram
const panel = page.locator('.react-flow__panel.bottom.left').first();
await panel.evaluate((node) => {
node.style.visibility = 'hidden';
});

const attr2NodeBefore = new PlaywrightNode(page, 'attr2', 'IconLabel');
await attr2NodeBefore.controlClick();

// And move them inside the 'Target' entity
await attr2NodeBefore.move({ x: 400, y: 0 });
await attr2NodeBefore.move({ x: 10, y: -300 });

await page.waitForFunction(
() => {
Expand Down Expand Up @@ -148,12 +153,17 @@ test.describe('diagram - drag and drop of multiple elements', () => {
// Now, inside the diagram, select both attributes from the 'Source' entity
const attr1NodeBefore = new PlaywrightNode(page, 'attr1', 'IconLabel');
await attr1NodeBefore.click();
// Hide Node Panel Info to avoid overlap in diagram
const panel = page.locator('.react-flow__panel.bottom.left').first();
await panel.evaluate((node) => {
node.style.visibility = 'hidden';
});

const attr2NodeBefore = new PlaywrightNode(page, 'attr2', 'IconLabel');
await attr2NodeBefore.controlClick();

// And move them directly on the diagram area (not on an entity)
await attr2NodeBefore.move({ x: 200, y: 200 });
await attr2NodeBefore.move({ x: 400, y: 100 });

await page.waitForFunction(
() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ test.describe('diagram - tool', () => {
test.describe('diagram - tool key bindings', () => {
let projectId;
test.beforeEach(async ({ page, request }) => {
await page.addInitScript(() => {
// @ts-expect-error: we use a variable in the DOM to disable `fitView` functionality for Cypress tests.
window.document.DEACTIVATE_FIT_VIEW_FOR_CYPRESS_TESTS = true;
});
const project = await new PlaywrightProject(request).createProject('Studio', 'blank-studio-template');
projectId = project.projectId;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.eclipse.sirius.components.diagrams.components.DiagramComponentProps;
import org.eclipse.sirius.components.diagrams.components.DiagramComponentProps.Builder;
import org.eclipse.sirius.components.diagrams.description.DiagramDescription;
import org.eclipse.sirius.components.diagrams.description.DiagramLayoutOption;
import org.eclipse.sirius.components.diagrams.events.IDiagramEvent;
import org.eclipse.sirius.components.diagrams.events.undoredo.DiagramEdgeLayoutEvent;
import org.eclipse.sirius.components.diagrams.events.undoredo.DiagramLabelLayoutEvent;
Expand Down Expand Up @@ -186,7 +187,8 @@ private Diagram doRender(Object targetObject, IEditingContext editingContext, Di
.filter(DiagramEdgeLayoutEvent.class::isInstance)
.map(DiagramEdgeLayoutEvent.class::cast).toList();

var newLayoutData = optionalPreviousDiagram.map(Diagram::getLayoutData).orElse(new DiagramLayoutData(Map.of(), Map.of(), Map.of()));
var newLayoutData = optionalPreviousDiagram.map(Diagram::getLayoutData).orElse(new DiagramLayoutData(Map.of(), Map.of(), Map.of(),
!diagramDescription.getLayoutOption().equals(DiagramLayoutOption.NONE)));

diagramNodeLayoutEvents.forEach(nodeLayoutDataEvent ->
newLayoutData.nodeLayoutData().put(nodeLayoutDataEvent.nodeId(), nodeLayoutDataEvent.nodeLayoutData()));
Expand Down Expand Up @@ -230,7 +232,13 @@ public Diagram updateLayout(IEditingContext editingContext, Diagram diagram, Lay
(oldValue, newValue) -> newValue
));

var layoutData = new DiagramLayoutData(nodeLayoutData, edgeLayoutData, labelLayoutData);
boolean autoLaidOut = switch (layoutDiagramInput.diagramLayoutData().autoLayoutState()) {
case ACTIVATE -> true;
case DEACTIVATE -> false;
case UNCHANGED -> diagram.getLayoutData().autoLaidOut();
};
Comment on lines +235 to +239
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's something odd at the heart of this in terms of lifecycle. If I create a diagram with NONE as a LayoutOption and then if I perform one arrangeAll, my diagram see to be marked as autoLaidOut even after other changes (creating a new element for example).

There are still again issues related to state management and lifecycle.


var layoutData = new DiagramLayoutData(nodeLayoutData, edgeLayoutData, labelLayoutData, autoLaidOut);
var laidOutDiagram = Diagram.newDiagram(diagram)
.layoutData(layoutData)
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*******************************************************************************
* 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.sirius.components.collaborative.diagrams.dto;

/**
* Defines the possible actions for managing the autoLayout state.
*
* @author frouene
*/
public enum AutoLayoutState {
UNCHANGED,
ACTIVATE,
DEACTIVATE
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023, 2025 Obeo.
* Copyright (c) 2023, 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
Expand All @@ -19,6 +19,9 @@
*
* @author sbegaudeau
*/
public record DiagramLayoutDataInput(List<NodeLayoutDataInput> nodeLayoutData, List<EdgeLayoutDataInput> edgeLayoutData, List<LabelLayoutDataInput> labelLayoutData) {
public record DiagramLayoutDataInput(List<NodeLayoutDataInput> nodeLayoutData,
List<EdgeLayoutDataInput> edgeLayoutData,
List<LabelLayoutDataInput> labelLayoutData,
AutoLayoutState autoLayoutState) {

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023, 2025 Obeo.
* Copyright (c) 2023, 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
Expand All @@ -23,6 +23,6 @@
*
* @author sbegaudeau
*/
public record DiagramLayoutDataPayload(List<NodeLayoutData> nodeLayoutData, List<EdgeLayoutData> edgeLayoutData, List<LabelLayoutData> labelLayoutData) {
public record DiagramLayoutDataPayload(List<NodeLayoutData> nodeLayoutData, List<EdgeLayoutData> edgeLayoutData, List<LabelLayoutData> labelLayoutData, boolean autoLaidOut) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type DiagramLayoutData {
nodeLayoutData: [NodeLayoutData!]!
edgeLayoutData: [EdgeLayoutData!]!
labelLayoutData: [LabelLayoutData!]!
autoLaidOut: Boolean!
}

type NodeLayoutData {
Expand Down Expand Up @@ -417,7 +418,7 @@ type SingleClickOnTwoDiagramElementsCandidate {
type DiagramDescription implements RepresentationDescription {
id: ID!
label: String!
autoLayout: Boolean!
layoutOption: DiagramLayoutOption!
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we will have a way to retrieve ELK "Layout options" from the backend in the upcoming days / weeks, this would have to be renamed very quickly to something clearly related to the auto-layout behavior. We may merge this as is, but it will have to be renamed during the cool down. It can't stay this way now.

arrangeLayoutDirection: ArrangeLayoutDirection!
nodeDescriptions: [NodeDescription!]!
childNodeDescriptionIds: [ID!]!
Expand Down Expand Up @@ -445,6 +446,12 @@ enum ArrangeLayoutDirection {
UP
}

enum DiagramLayoutOption {
NONE,
AUTO_LAYOUT,
AUTO_UNTIL_MANUAL
}

type DropNodeCompatibilityEntry {
droppedNodeDescriptionId: ID!
droppableOnDiagram: Boolean!
Expand Down Expand Up @@ -723,6 +730,13 @@ input DiagramLayoutDataInput {
nodeLayoutData: [NodeLayoutDataInput!]!
edgeLayoutData: [EdgeLayoutDataInput!]!
labelLayoutData: [LabelLayoutDataInput!]!
autoLayoutState: AutoLayoutState!
}

enum AutoLayoutState {
UNCHANGED
ACTIVATE
DEACTIVATE
}

input NodeLayoutDataInput {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019, 2025 Obeo.
* Copyright (c) 2019, 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
Expand Down Expand Up @@ -34,6 +34,7 @@
import org.eclipse.sirius.components.core.api.IRepresentationDescriptionSearchService;
import org.eclipse.sirius.components.diagrams.Diagram;
import org.eclipse.sirius.components.diagrams.description.DiagramDescription;
import org.eclipse.sirius.components.diagrams.description.DiagramLayoutOption;
import org.eclipse.sirius.components.representations.Failure;
import org.eclipse.sirius.components.representations.IRepresentationDescription;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -64,6 +65,7 @@ public Optional<IRepresentationDescription> findById(IEditingContext editingCont
.targetObjectIdProvider(variableManager -> "targetObjectId")
.dropHandler(variableManager -> new Failure(""))
.iconURLsProvider(variableManager -> List.of())
.layoutOption(DiagramLayoutOption.NONE)
.build();

return Optional.of(diagramDescription);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2022, 2025 Obeo.
* Copyright (c) 2022, 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
Expand Down Expand Up @@ -48,6 +48,7 @@
import org.eclipse.sirius.components.diagrams.ViewModifier;
import org.eclipse.sirius.components.diagrams.components.BorderNodePosition;
import org.eclipse.sirius.components.diagrams.description.DiagramDescription;
import org.eclipse.sirius.components.diagrams.description.DiagramLayoutOption;
import org.eclipse.sirius.components.diagrams.description.NodeDescription;
import org.eclipse.sirius.components.representations.Failure;
import org.eclipse.sirius.components.representations.IRepresentationDescription;
Expand Down Expand Up @@ -143,6 +144,7 @@ public void testGetConnectorTools() {
.edgeDescriptions(new ArrayList<>())
.dropHandler(variableManager -> new Failure(""))
.iconURLsProvider(variableManager -> List.of())
.layoutOption(DiagramLayoutOption.NONE)
.build();

Node sourceNode = this.getNode(SOURCE_NODE_ID, SOURCE_NODE_TARGET_ID);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2024, 2025 Obeo.
* Copyright (c) 2024, 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
Expand Down Expand Up @@ -81,6 +81,7 @@ public Optional<IRepresentationDescription> findById(IEditingContext editingCont
}
return desc;
}

@Override
public Map<String, IRepresentationDescription> findAll(IEditingContext editingContext) {
var descriptions = new HashMap<String, IRepresentationDescription>();
Expand All @@ -103,7 +104,7 @@ public Map<String, IRepresentationDescription> findAll(IEditingContext editingCo
var diagram = Diagram.newDiagram(UUID.randomUUID().toString())
.descriptionId(DIAGRAM_DESCRIPTION_ID)
.edges(List.of())
.layoutData(new DiagramLayoutData(Map.of(), Map.of(), Map.of()))
.layoutData(new DiagramLayoutData(Map.of(), Map.of(), Map.of(), false))
.nodes(List.of())
.targetObjectId("")
.build();
Expand Down
Loading
Loading