Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[781] Add support for multiline labels #1087

Merged
merged 1 commit into from
Mar 8, 2022
Merged
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 @@ -77,6 +77,7 @@
- https://github.com/eclipse-sirius/sirius-components/issues/1026[#1026] [compatibility] Add support for `OperationAction`. The action are converted to regular tools available in the palette of the frontend
- https://github.com/eclipse-sirius/sirius-components/issues/937[#937] [diagram] Add the ability to export diagram as SVG images
- https://github.com/eclipse-sirius/sirius-components/issues/779[#779] [diagram] Add support for tools preconditions
- https://github.com/eclipse-sirius/sirius-components/issues/781[#781] [diagram] Add support for multiline labels


== v2022.01.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public StringBuilder exportLabel(Label label) {
labelExport.append(this.exportImageElement(style.getIconURL(), -20, -12, Optional.empty()));
}

labelExport.append(this.exportTextElement(label.getText(), style));
labelExport.append(this.exportTextElement(label.getText(), label.getType(), style));

return labelExport.append("</g>"); //$NON-NLS-1$
}
Expand Down Expand Up @@ -109,16 +109,32 @@ private StringBuilder addSizeParam(Size size) {
return sizeParam.append("height=\"" + size.getHeight() + "\" "); //$NON-NLS-1$ //$NON-NLS-2$
}

private StringBuilder exportTextElement(String text, LabelStyle labelStyle) {
private StringBuilder exportTextElement(String text, String type, LabelStyle labelStyle) {
StringBuilder textExport = new StringBuilder();

textExport.append("<text "); //$NON-NLS-1$
textExport.append("style=\""); //$NON-NLS-1$
textExport.append("fill: " + labelStyle.getColor() + "; "); //$NON-NLS-1$ //$NON-NLS-2$
textExport.append(this.exportFont(labelStyle));
if (type.contains("center")) { //$NON-NLS-1$
textExport.append("text-anchor:middle"); //$NON-NLS-1$
}
textExport.append("\">"); //$NON-NLS-1$

textExport.append(text);
String[] lines = text.split("\\n", -1); //$NON-NLS-1$
if (lines.length == 1) {
textExport.append(text);
} else {
textExport.append("<tspan x=\"0\">" + lines[0] + "</tspan>"); //$NON-NLS-1$//$NON-NLS-2$
double fontSize = labelStyle.getFontSize();
for (int i = 1; i < lines.length; i++) {
if (lines[i].isEmpty()) {
// avoid tspan to be ignored if there is only a line return
lines[i] = " "; //$NON-NLS-1$
}
textExport.append("<tspan x=\"0\" dy=\"" + fontSize + "\">" + lines[i] + "</tspan>"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
}
}

return textExport.append("</text>"); //$NON-NLS-1$
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
package org.eclipse.sirius.components.collaborative.forms.handlers;

import java.util.Objects;
import java.util.function.Function;

import org.eclipse.sirius.components.collaborative.api.ChangeDescription;
import org.eclipse.sirius.components.collaborative.api.ChangeKind;
Expand All @@ -26,6 +27,7 @@
import org.eclipse.sirius.components.core.api.ErrorPayload;
import org.eclipse.sirius.components.core.api.IPayload;
import org.eclipse.sirius.components.forms.Form;
import org.eclipse.sirius.components.forms.Textarea;
import org.eclipse.sirius.components.forms.Textfield;
import org.eclipse.sirius.components.representations.Failure;
import org.eclipse.sirius.components.representations.IStatus;
Expand Down Expand Up @@ -78,11 +80,16 @@ public void handle(One<IPayload> payloadSink, Many<ChangeDescription> changeDesc
EditTextfieldInput input = (EditTextfieldInput) formInput;

// @formatter:off
var optionalTextfield = this.formQueryService.findWidget(form, input.getTextfieldId())
.filter(Textfield.class::isInstance)
.map(Textfield.class::cast);

IStatus status = optionalTextfield.map(Textfield::getNewValueHandler)
IStatus status = this.formQueryService.findWidget(form, input.getTextfieldId())
.map(widget -> {
Function<String, IStatus> handlerFunction = null;
if (widget instanceof Textfield) {
handlerFunction = ((Textfield) widget).getNewValueHandler();
} else if (widget instanceof Textarea) {
handlerFunction = ((Textarea) widget).getNewValueHandler();
}
return handlerFunction;
})
.map(handler -> handler.apply(input.getNewValue()))
.orElse(new Failure("")); //$NON-NLS-1$
// @formatter:on
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,8 @@ private void convertNode(Node node, ElkNode parent, Map<String, ElkConnectableSh
elkNode.setDimensions(node.getSize().getWidth(), node.getSize().getHeight());
elkNode.setLocation(node.getPosition().getX(), node.getPosition().getY());
} else {
double width = Math.max(textBounds.getSize().getWidth(), node.getSize().getWidth());
double height = Math.max(textBounds.getSize().getHeight(), node.getSize().getHeight());
double width = textBounds.getSize().getWidth();
double height = textBounds.getSize().getHeight();
elkNode.setDimensions(width, height);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,17 @@ private Node getLayoutedNode(Node node, ElkConnectableShape elkConnectableShape,
Size size = Size.of(elkConnectableShape.getWidth(), elkConnectableShape.getHeight());
Position position = Position.at(elkConnectableShape.getX(), elkConnectableShape.getY());

// @formatter:off
Label label = this.getLayoutedLabel(node.getLabel(), id2ElkGraphElements, 0, 0);
double xOffSet = 0;
if (!node.isBorderNode()) {
// The label is positioned at the center of the node and the front-end will apply a "'text-anchor':
// 'middle'" property.
xOffSet = node.getLabel().getSize().getWidth() / 2;
}
Comment on lines +80 to +84
Copy link
Member

Choose a reason for hiding this comment

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

Can make this hypothesis if in the class below, we have the code to deal with various label placement strategies? What was the issue with the previous algorithm?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did not catch your first sentence.

What was the issue with the previous algorithm?

The frontend needs to display the label area centered on the node and each line part centered inside the node
I used "text-anchor:middle" to do that. But to make it work the label position needs to be positionned on the center of the node. I tried to apply text-anchor:middle only on tspan instead of the parent text but text-anchor:middle make still the line positionned on the label x position

Label label = this.getLayoutedLabel(node.getLabel(), id2ElkGraphElements, xOffSet, 0);

List<Node> childNodes = this.getLayoutedNodes(node.getChildNodes(), id2ElkGraphElements);
List<Node> borderNodes = this.getLayoutedNodes(node.getBorderNodes(), id2ElkGraphElements);
// @formatter:off
return Node.newNode(node)
.label(label)
.size(size)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,20 +152,21 @@ private void layoutNode(Optional<IDiagramEvent> optionalDiagramElementEvent, Nod

// update the border node once the current node bounds are updated
Bounds newBounds = Bounds.newBounds().position(node.getPosition()).size(node.getSize()).build();
this.layoutBorderNodes(optionalDiagramElementEvent, node.getBorderNodes(), initialNodeBounds, newBounds, layoutConfigurator);
List<BorderNodesOnSide> borderNodesOnSide = this.layoutBorderNodes(optionalDiagramElementEvent, node.getBorderNodes(), initialNodeBounds, newBounds, layoutConfigurator);

// recompute the label
if (node.getLabel() != null) {
node.getLabel().setPosition(this.nodeLabelPositionProvider.getPosition(node, node.getLabel()));
node.getLabel().setPosition(this.nodeLabelPositionProvider.getPosition(node, node.getLabel(), borderNodesOnSide));
}
}

/**
* Update the border nodes position according to the side length change where it is located.<br>
* The aim is to keep the positioning ratio of the border node on its side.
*/
private void layoutBorderNodes(Optional<IDiagramEvent> optionalDiagramElementEvent, List<NodeLayoutData> borderNodesLayoutData, Bounds initialNodeBounds, Bounds newNodeBounds,
private List<BorderNodesOnSide> layoutBorderNodes(Optional<IDiagramEvent> optionalDiagramElementEvent, List<NodeLayoutData> borderNodesLayoutData, Bounds initialNodeBounds, Bounds newNodeBounds,
ISiriusWebLayoutConfigurator layoutConfigurator) {
List<BorderNodesOnSide> borderNodesPerSide = new ArrayList<>();
if (!borderNodesLayoutData.isEmpty()) {
for (NodeLayoutData nodeLayoutData : borderNodesLayoutData) {
// 1- update the position of the border node if it has been explicitly moved
Expand All @@ -179,14 +180,15 @@ private void layoutBorderNodes(Optional<IDiagramEvent> optionalDiagramElementEve
}

// 2- recompute the border node
List<BorderNodesOnSide> borderNodesPerSide = this.snapBorderNodes(borderNodesLayoutData, initialNodeBounds.getSize(), layoutConfigurator);
borderNodesPerSide = this.snapBorderNodes(borderNodesLayoutData, initialNodeBounds.getSize(), layoutConfigurator);

// 3 - move the border node along the side according to the side change
this.updateBorderNodeAccordingParentResize(optionalDiagramElementEvent, initialNodeBounds, newNodeBounds, borderNodesPerSide, borderNodesLayoutData.get(0).getParent().getId());

// 4- set the label position if the border is newly created
this.updateBorderNodeLabel(optionalDiagramElementEvent, borderNodesPerSide);
}
return borderNodesPerSide;
}

private void updateBorderNodeLabel(Optional<IDiagramEvent> optionalDiagramElementEvent, List<BorderNodesOnSide> borderNodesPerSideList) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2021 THALES GLOBAL SERVICES.
* Copyright (c) 2021, 2022 THALES GLOBAL SERVICES.
* 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 @@ -13,6 +13,7 @@
package org.eclipse.sirius.components.diagrams.layout.incremental.provider;

import java.util.EnumSet;
import java.util.List;
import java.util.Objects;

import org.eclipse.elk.core.math.ElkPadding;
Expand All @@ -21,8 +22,10 @@
import org.eclipse.sirius.components.diagrams.NodeType;
import org.eclipse.sirius.components.diagrams.Position;
import org.eclipse.sirius.components.diagrams.layout.ISiriusWebLayoutConfigurator;
import org.eclipse.sirius.components.diagrams.layout.incremental.BorderNodesOnSide;
import org.eclipse.sirius.components.diagrams.layout.incremental.data.LabelLayoutData;
import org.eclipse.sirius.components.diagrams.layout.incremental.data.NodeLayoutData;
import org.eclipse.sirius.components.diagrams.layout.incremental.utils.RectangleSide;

/**
* Provides the position to apply to a Node Label.
Expand All @@ -37,7 +40,7 @@ public NodeLabelPositionProvider(ISiriusWebLayoutConfigurator layoutConfigurator
this.layoutConfigurator = Objects.requireNonNull(layoutConfigurator);
}

public Position getPosition(NodeLayoutData node, LabelLayoutData label) {
public Position getPosition(NodeLayoutData node, LabelLayoutData label, List<BorderNodesOnSide> borderNodesOnSide) {
double x = 0d;
double y = 0d;

Expand All @@ -49,15 +52,15 @@ public Position getPosition(NodeLayoutData node, LabelLayoutData label) {
}
break;
default:
x = this.getHorizontalPosition(node, label);
x = this.getHorizontalPosition(node, label, borderNodesOnSide);
y = this.getVerticalPosition(node, label);
break;
}

return Position.at(x, y);
}

private double getHorizontalPosition(NodeLayoutData node, LabelLayoutData label) {
private double getHorizontalPosition(NodeLayoutData node, LabelLayoutData label, List<BorderNodesOnSide> borderNodesOnSides) {
double x = 0d;
EnumSet<NodeLabelPlacement> nodeLabelPlacementSet = this.layoutConfigurator.configureByType(node.getNodeType()).getProperty(CoreOptions.NODE_LABELS_PLACEMENT);
ElkPadding nodeLabelsPadding = this.layoutConfigurator.configureByType(node.getNodeType()).getProperty(CoreOptions.NODE_LABELS_PADDING);
Expand All @@ -79,7 +82,21 @@ private double getHorizontalPosition(NodeLayoutData node, LabelLayoutData label)
}
break;
case H_CENTER:
x = (node.getSize().getWidth() - label.getTextBounds().getSize().getWidth()) / 2;
// The label is positioned at the center of the node and the front-end will apply a "'text-anchor':
// 'middle'" property.
int shiftToEast = 0;
int shiftToWest = 0;
for (BorderNodesOnSide borderNodesOnSide : borderNodesOnSides) {
if (RectangleSide.WEST.equals(borderNodesOnSide.getSide())) {
shiftToEast = 1;
} else if (RectangleSide.EAST.equals(borderNodesOnSide.getSide())) {
shiftToWest = 1;
}
}
double portOffset = this.layoutConfigurator.configureByType(node.getNodeType()).getProperty(CoreOptions.PORT_BORDER_OFFSET).doubleValue();
double offSetAccordingToBorderNodes = -portOffset / 2 * (shiftToEast - shiftToWest);

x = node.getSize().getWidth() / 2 + offSetAccordingToBorderNodes;
break;
case H_RIGHT:
if (outside) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2021 THALES GLOBAL SERVICES.
* Copyright (c) 2021, 2022 THALES GLOBAL SERVICES.
* 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 @@ -14,6 +14,7 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

Expand Down Expand Up @@ -61,8 +62,8 @@ public void testNodeImageLabelBoundsPosition() {
NodeLabelPositionProvider labelBoundsProvider = new NodeLabelPositionProvider(new LayoutConfiguratorRegistry(List.of()).getDefaultLayoutConfigurator());
LabelLayoutData labelLayoutData = this.createLabelLayoutData();

Position position = labelBoundsProvider.getPosition(nodeLayoutData, labelLayoutData);
assertThat(position).extracting(Position::getX).isEqualTo(Double.valueOf(42.5390625));
Position position = labelBoundsProvider.getPosition(nodeLayoutData, labelLayoutData, new ArrayList<>());
assertThat(position).extracting(Position::getX).isEqualTo(Double.valueOf(DEFAULT_NODE_SIZE.getWidth() / 2));
assertThat(position).extracting(Position::getY).isEqualTo(Double.valueOf(-23.3984375));
}

Expand All @@ -72,8 +73,8 @@ public void testNodeRectangleLabelBoundsPosition() {
NodeLayoutData nodeLayoutData = this.createNodeLayoutData(Position.at(0, 0), DEFAULT_NODE_SIZE, createDiagramLayoutData, NodeType.NODE_RECTANGLE);
NodeLabelPositionProvider labelBoundsProvider = new NodeLabelPositionProvider(new LayoutConfiguratorRegistry(List.of()).getDefaultLayoutConfigurator());
LabelLayoutData labelLayoutData = this.createLabelLayoutData();
Position position = labelBoundsProvider.getPosition(nodeLayoutData, labelLayoutData);
assertThat(position).extracting(Position::getX).isEqualTo(Double.valueOf(42.5390625));
Position position = labelBoundsProvider.getPosition(nodeLayoutData, labelLayoutData, new ArrayList<>());
assertThat(position).extracting(Position::getX).isEqualTo(Double.valueOf(DEFAULT_NODE_SIZE.getWidth() / 2));
assertThat(position).extracting(Position::getY).isEqualTo(Double.valueOf(5));
}

Expand Down
Loading