Skip to content

Commit

Permalink
[2883] Store in node layout data if a user has resized the node
Browse files Browse the repository at this point in the history
Bug: #2883
Signed-off-by: Guillaume Coutable <guillaume.coutable@obeo.fr>
  • Loading branch information
gcoutable committed Jan 9, 2024
1 parent 017e3ed commit 5974271
Show file tree
Hide file tree
Showing 24 changed files with 145 additions and 42 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ The GraphQL type `NodeDescription` has a new field `childNodeDescriptionIds`, re
The GraphQL type `NodeDescription` has a new field `borderNodeDescriptionIds`, returning a list of Ids and replacing the field `borderNodeDescriptions` which has been deleted.
- https://github.com/eclipse-sirius/sirius-web/issues/2872[#2872] [diagram] Change the signature of the node converters in order to leverage the diagram description to resolve the reuse descriptions.
Additional changes to this interface will propably occur in the near future in order to support more complex use cases.
- https://github.com/eclipse-sirius/sirius-web/issues/2883[#2883] [diagram] Add the attribute `resizedByUser` to reactflow nodes and graphql nodes.


=== Dependency update

Expand All @@ -96,6 +98,7 @@ Now, for a multi-valued feature, the values are properly displayed and the delet
- https://github.com/eclipse-sirius/sirius-web/issues/2797[#2797] [diagram] Preserve diagram selection after change in the details view.

=== New Features

- https://github.com/eclipse-sirius/sirius-web/issues/2413[#2413] Add a debug mode for React Flow diagrams

- https://github.com/eclipse-sirius/sirius-web/issues/2735[#2735] [gantt] Contribute the first version of the Gantt representation.
Expand All @@ -105,6 +108,9 @@ Now, for a multi-valued feature, the values are properly displayed and the delet
- https://github.com/eclipse-sirius/sirius-web/issues/2819[#2819] [deck] Contribute the first version of the Deck representation.
- https://github.com/eclipse-sirius/sirius-web/issues/2765[#2765] [portal] Contribute the first version of the Portal representation.
- https://github.com/eclipse-sirius/sirius-web/issues/2737[#2737] [gantt] Support create, edit and delete task in gantt.
- https://github.com/eclipse-sirius/sirius-web/issues/2883[#2883] [diagram] The size of manually resized node does not change anymore, unless it needs to.
+
The size of nodes that do not have been resized manually tries to fit the node default size.

=== Improvements

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2019, 2023 Obeo.
* Copyright (c) 2019, 2024 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 @@ -132,7 +132,7 @@ public void handle(One<IPayload> payloadSink, Many<ChangeDescription> changeDesc
var nodeLayoutData = layoutDiagramInput.diagramLayoutData().nodeLayoutData().stream()
.collect(Collectors.toMap(
NodeLayoutDataInput::id,
nodeLayoutDataInput -> new NodeLayoutData(nodeLayoutDataInput.id(), nodeLayoutDataInput.position(), nodeLayoutDataInput.size())
nodeLayoutDataInput -> new NodeLayoutData(nodeLayoutDataInput.id(), nodeLayoutDataInput.position(), nodeLayoutDataInput.size(), nodeLayoutDataInput.resizedByUser())
));

var layoutData = new DiagramLayoutData(nodeLayoutData, Map.of(), Map.of());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* Copyright (c) 2023, 2024 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 @@ -20,5 +20,5 @@
*
* @author sbegaudeau
*/
public record NodeLayoutDataInput(String id, Position position, Size size) {
public record NodeLayoutDataInput(String id, Position position, Size size, boolean resizedByUser) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ type NodeLayoutData {
id: ID!
position: Position!
size: Size!
resizedByUser: Boolean!
}

enum ViewModifier {
Expand Down Expand Up @@ -77,6 +78,7 @@ type Node {
defaultWidth: Int
defaultHeight: Int
labelEditable: Boolean!
resizedByUser: Boolean!
}

type FreeFormLayoutStrategy {
Expand Down Expand Up @@ -591,6 +593,7 @@ input NodeLayoutDataInput {
id: ID!
position: PositionInput!
size: SizeInput!
resizedByUser: Boolean!
}

input SizeInput {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*******************************************************************************
* Copyright (c) 2024 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.diagrams.graphql.datafetchers.diagram;

import java.util.Map;
import java.util.Optional;

import org.eclipse.sirius.components.annotations.spring.graphql.QueryDataFetcher;
import org.eclipse.sirius.components.diagrams.Diagram;
import org.eclipse.sirius.components.diagrams.Node;
import org.eclipse.sirius.components.diagrams.layoutdata.DiagramLayoutData;
import org.eclipse.sirius.components.diagrams.layoutdata.NodeLayoutData;
import org.eclipse.sirius.components.graphql.api.IDataFetcherWithFieldCoordinates;

import graphql.schema.DataFetchingEnvironment;

/**
* Used to retrieve the resized by user from diagram layout data.
*
* @author gcoutable
*/
@QueryDataFetcher(type = "Node", field = "resizedByUser")
public class NodeResizedByUserDataFetcher implements IDataFetcherWithFieldCoordinates<Boolean> {

@Override
public Boolean get(DataFetchingEnvironment environment) throws Exception {
Node node = environment.getSource();
Map<String, Object> localContext = environment.getLocalContext();

return Optional.ofNullable(localContext.get("diagram"))
.filter(Diagram.class::isInstance)
.map(Diagram.class::cast)
.flatMap(diagram -> this.resizedByUser(diagram, node))
.orElse(false);
}

private Optional<Boolean> resizedByUser(Diagram diagram, Node node) {
return Optional.of(diagram.getLayoutData())
.map(DiagramLayoutData::nodeLayoutData)
.map(layoutData -> layoutData.get(node.getId()))
.map(NodeLayoutData::resizedByUser);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* Copyright (c) 2023, 2024 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 @@ -67,7 +67,7 @@ public DiagramLayoutData layout(DiagramLayoutConfiguration diagramLayoutConfigur
}

Map<String, NodeLayoutData> newNodeLayoutData = new HashMap<>();
diagramBoxModel.nodeBoxModels().forEach((nodeId, nodeBoxModel) -> newNodeLayoutData.put(nodeId, new NodeLayoutData(nodeId, nodeBoxModel.bounds().topLeft(), nodeBoxModel.bounds().size())));
diagramBoxModel.nodeBoxModels().forEach((nodeId, nodeBoxModel) -> newNodeLayoutData.put(nodeId, new NodeLayoutData(nodeId, nodeBoxModel.bounds().topLeft(), nodeBoxModel.bounds().size(), false)));

return new DiagramLayoutData(newNodeLayoutData, Map.of(), Map.of());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* Copyright (c) 2023, 2024 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 @@ -20,6 +20,7 @@
public record NodeLayoutData(
String id,
Position position,
Size size
Size size,
boolean resizedByUser
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const toIconLabelNode = (
state,
style,
labelEditable,
resizedByUser,
} = gqlNode;

const connectionHandles: ConnectionHandle[] = [];
Expand All @@ -72,6 +73,7 @@ const toIconLabelNode = (
labelEditable: labelEditable,
connectionHandles,
isNew,
resizedByUser,
};

if (insideLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const toImageNode = (
state,
style,
labelEditable,
resizedByUser,
} = gqlNode;

const connectionHandles: ConnectionHandle[] = convertHandles(gqlNode, gqlEdges);
Expand All @@ -69,6 +70,7 @@ const toImageNode = (
positionDependentRotation: style.positionDependentRotation,
connectionHandles,
isNew,
resizedByUser,
};

if (insideLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ const toListNode = (
state,
style,
labelEditable,
resizedByUser,
} = gqlNode;

const connectionHandles: ConnectionHandle[] = convertHandles(gqlNode, gqlEdges);
Expand Down Expand Up @@ -92,6 +93,7 @@ const toListNode = (
areChildNodesDraggable: isListLayoutStrategy(gqlNode.childrenLayoutStrategy)
? gqlNode.childrenLayoutStrategy.areChildNodesDraggable
: true,
resizedByUser,
};

if (insideLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const toRectangularNode = (
state,
style,
labelEditable,
resizedByUser,
} = gqlNode;

const connectionHandles: ConnectionHandle[] = convertHandles(gqlNode, gqlEdges);
Expand Down Expand Up @@ -80,6 +81,7 @@ const toRectangularNode = (
labelEditable,
connectionHandles,
isNew,
resizedByUser,
};

if (insideLabel) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,6 @@ fragment nodeFragment on Node {
defaultWidth
defaultHeight
labelEditable
resizedByUser
}
`;
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface GQLNode<T extends GQLNodeStyle> {
defaultWidth: number | null;
defaultHeight: number | null;
labelEditable: boolean;
resizedByUser: boolean;
}

export interface ILayoutStrategy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export interface NodeData {
style: React.CSSProperties;
connectionHandles: ConnectionHandle[];
isNew: boolean;
resizedByUser: boolean;
}

export enum BorderNodePosition {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* Copyright (c) 2023, 2024 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 @@ -11,7 +11,7 @@
* Obeo - initial API and implementation
*******************************************************************************/

import { Node, NodeChange, useReactFlow } from 'reactflow';
import { Node, NodeChange, NodeDimensionChange, NodePositionChange, useReactFlow } from 'reactflow';
import { EdgeData, NodeData } from '../DiagramRenderer.types';
import { useDropNode } from '../dropNode/useDropNode';
import { RawDiagram } from '../layout/layout.types';
Expand All @@ -26,31 +26,51 @@ export const useLayoutOnBoundsChange = (refreshEventPayloadId: string): UseLayou
const { hasDroppedNodeParentChanged } = useDropNode();
const { synchronizeLayoutData } = useSynchronizeLayoutData();

const isMoveFinished = (change: NodeChange): boolean => {
const isMoveFinished = (change: NodeChange): change is NodePositionChange => {
return (
change.type === 'position' &&
typeof change.dragging === 'boolean' &&
!change.dragging &&
!hasDroppedNodeParentChanged()
);
};
const isResizeFinished = (change: NodeChange): boolean =>
const isResizeFinished = (change: NodeChange): change is NodeDimensionChange =>
change.type === 'dimensions' && typeof change.resizing === 'boolean' && !change.resizing;

const isBoundsChangeFinished = (changes: NodeChange[]): NodeChange | undefined => {
return changes.find((change) => isMoveFinished(change) || isResizeFinished(change));
};

const updateNodeResizeByUserState = (
changes: NodeChange[],
nodes: Node<NodeData, DiagramNodeType>[]
): Node<NodeData, DiagramNodeType>[] => {
return nodes.map((node) => {
if (changes.filter(isResizeFinished).find((dimensionChange) => dimensionChange.id === node.id)) {
return {
...node,
data: {
...node.data,
resizedByUser: true,
},
};
}
return node;
});
};

const layoutOnBoundsChange = (changes: NodeChange[], nodes: Node<NodeData, DiagramNodeType>[]): void => {
const change = isBoundsChangeFinished(changes);
if (change) {
const updatedNodes = updateNodeResizeByUserState(changes, nodes);

const diagramToLayout: RawDiagram = {
nodes,
nodes: updatedNodes,
edges: getEdges(),
};

layout(diagramToLayout, diagramToLayout, null, (laidOutDiagram) => {
nodes.map((node) => {
updatedNodes.map((node) => {
const existingNode = laidOutDiagram.nodes.find((laidoutNode) => laidoutNode.id === node.id);
if (existingNode) {
return {
Expand All @@ -67,10 +87,10 @@ export const useLayoutOnBoundsChange = (refreshEventPayloadId: string): UseLayou
}
return node;
});
setNodes(nodes);
setNodes(updatedNodes);
setEdges(laidOutDiagram.edges);
const finalDiagram: RawDiagram = {
nodes: nodes,
nodes: updatedNodes,
edges: laidOutDiagram.edges,
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* Copyright (c) 2023, 2024 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 @@ -124,7 +124,7 @@ export class ImageNodeLayoutHandler implements INodeLayoutHandler<ImageNodeData>
const previousNode = (previousDiagram?.nodes ?? []).find((previous) => previous.id === node.id);
const previousDimensions = computePreviousSize(previousNode, node);

if (node.data.nodeDescription?.userResizable) {
if (node.data.resizedByUser) {
if (minNodeWith > previousDimensions.width) {
node.width = minNodeWith;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export class ListNodeLayoutHandler implements INodeLayoutHandler<ListNodeData> {
const previousNode = (previousDiagram?.nodes ?? []).find((previouseNode) => previouseNode.id === node.id);
const previousDimensions = computePreviousSize(previousNode, node);

if (node.data.nodeDescription?.userResizable) {
if (node.data.resizedByUser) {
if (minNodeWith > previousDimensions.width) {
node.width = minNodeWith;
} else {
Expand Down Expand Up @@ -127,7 +127,7 @@ export class ListNodeLayoutHandler implements INodeLayoutHandler<ListNodeData> {

if (!forceWidth) {
let previousChildrenContentBoxWidthToConsider: number = 0;
if (node.data.nodeDescription?.userResizable) {
if (node.data.resizedByUser) {
previousChildrenContentBoxWidthToConsider = (previousNode?.width ?? 0) - borderWidth * 2;
}
const widerWidth = Math.max(
Expand Down Expand Up @@ -173,8 +173,7 @@ export class ListNodeLayoutHandler implements INodeLayoutHandler<ListNodeData> {
node.width = forceWidth ?? getNodeOrMinWidth(nodeWidth, node);

const minNodeheight = getNodeOrMinHeight(nodeHeight, node);
// TODO: rework this.
if (node.data.nodeDescription?.userResizable && previousNode) {
if (node.data.resizedByUser && previousNode) {
if (minNodeheight > (previousNode.height ?? 0)) {
node.height = minNodeheight;
} else {
Expand Down
Loading

0 comments on commit 5974271

Please sign in to comment.