Skip to content

Commit

Permalink
Only create preview edge once the source element has been determined
Browse files Browse the repository at this point in the history
  • Loading branch information
hlxid committed Nov 2, 2023
1 parent c0b05e6 commit 64a9612
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 22 deletions.
41 changes: 26 additions & 15 deletions src/features/toolPalette/creationTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Impl = SNodeImpl | SEdgeImpl | SPortImpl;
export abstract class CreationTool<S extends Schema, I extends Impl> extends MouseListener implements DfdTool {
protected element?: I;
protected readonly previewOpacity = 0.5;
protected insertIntoGraphRootAfterCreation = true;

constructor(
@inject(MouseTool) protected mouseTool: MouseTool,
Expand Down Expand Up @@ -66,8 +67,10 @@ export abstract class CreationTool<S extends Schema, I extends Impl> extends Mou
this.dynamicChildrenProcessor.processGraphChildren(schema, "set");

const element = this.modelFactory.createElement(schema) as I;
const root = await this.commandStack.executeAll([]);
root.add(element);
if (this.insertIntoGraphRootAfterCreation) {
const root = await this.commandStack.executeAll([]);
root.add(element);
}

return element;
}
Expand All @@ -88,14 +91,28 @@ export abstract class CreationTool<S extends Schema, I extends Impl> extends Mou
this.mouseTool.deregister(this);

if (this.element) {
const root = this.element.root;
// Element is not placed yet but we're disabling the tool.
// This means the creation was cancelled and the element should be deleted.

// Get root before removing the element, needed for re-render
let root: SGraphImpl | undefined;
try {
root = this.element.root as SGraphImpl;
} catch (error) {
// element has no assigned root
}

// Remove element from graph
this.element.parent?.remove(this.element);
this.element = undefined;

// Re-render the graph to remove the element from the preview
this.commandStack.update(root);
// Re-render the graph to remove the element from the preview.
// Root may be unavailable e.g. when the element hasn't been inserted into
// the diagram yet. Skipping the render in those cases is fine as the element
// wasn't rendered in such case anyway.
if (root) {
this.commandStack.update(root);
}

this.logger.info(this, "Cancelled element creation");
}
Expand Down Expand Up @@ -127,11 +144,11 @@ export abstract class CreationTool<S extends Schema, I extends Impl> extends Mou
return [];
}

const newPosition = { ...this.calculateMousePosition(event) };
const newPosition = { ...this.calculateMousePosition(target, event) };

if (this.element instanceof SEdgeImpl) {
// Snap the edge target to the mouse position, if there is a target element.
if (this.element.target) {
if (this.element.targetId && this.element.target) {
if (!Point.equals(this.element.target.position, newPosition)) {
this.element.target.position = newPosition;
// Trigger re-rendering of the edge
Expand Down Expand Up @@ -186,14 +203,8 @@ export abstract class CreationTool<S extends Schema, I extends Impl> extends Mou
/**
* Calculates the mouse position in graph coordinates.
*/
protected calculateMousePosition(event: MouseEvent): Point {
const root = this.element?.root as SGraphImpl | undefined;
if (!root) {
return {
x: -Infinity,
y: -Infinity,
};
}
protected calculateMousePosition(target: SModelElementImpl, event: MouseEvent): Point {
const root = target.root as SGraphImpl;

const calcPos = (axis: "x" | "y") => {
// Position of the top left viewport corner in the whole graph
Expand Down
21 changes: 14 additions & 7 deletions src/features/toolPalette/edgeCreationTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ export class EdgeCreationTool extends CreationTool<SEdge, SEdgeImpl> {
// Pseudo element that is used as a target for the edge while it is being created
private edgeTargetElement?: SChildElementImpl;

// We will insert the edge ourselves once we determined the source element.
// Then we can also insert the edge target element at the mouse position
// and have the source and target element inserted.
// Otherwise sprotty would not be able to compute the path of the edge
// and show dangling elements.
protected override insertIntoGraphRootAfterCreation = false;

createElementSchema(): SEdge {
return {
id: generateRandomSprottyId(),
Expand Down Expand Up @@ -48,7 +55,7 @@ export class EdgeCreationTool extends CreationTool<SEdge, SEdgeImpl> {
return [];
}

if (this.element.source) {
if (this.element.sourceId) {
// Source already set, so we're setting the target now

if (clickedElement.canConnect(this.element, "target")) {
Expand All @@ -59,10 +66,13 @@ export class EdgeCreationTool extends CreationTool<SEdge, SEdgeImpl> {
}
} else {
// Source not set yet, so we're setting the source now

if (clickedElement.canConnect(this.element, "source")) {
this.element.sourceId = clickedElement.id;

// Insert the edge to make it visible.
const root = target.root;
root.add(this.element);

// Create a new target element
// For previewing the edge it must be able to be rendered
// which means source and target *must* be set even though
Expand All @@ -73,16 +83,13 @@ export class EdgeCreationTool extends CreationTool<SEdge, SEdgeImpl> {
this.edgeTargetElement = this.modelFactory.createElement({
id: generateRandomSprottyId(),
type: "empty-node",
position: this.calculateMousePosition(event),
position: this.calculateMousePosition(target, event),
} as SNode);
// Add empty node to the graph and as a edge target
this.element.root.add(this.edgeTargetElement);
root.add(this.edgeTargetElement);
this.element.targetId = this.edgeTargetElement.id;
}
}

// Trigger re-rendering of the edge
this.commandStack.update(this.element.root);
return [];
}

Expand Down

0 comments on commit 64a9612

Please sign in to comment.