UNPKG

gojs

Version:

Interactive diagrams, charts, and graphs, such as trees, flowcharts, orgcharts, UML, BPMN, or business diagrams

293 lines (265 loc) 7.86 kB
"use strict" /* * Copyright (C) 1998-2017 by Northwoods Software Corporation. All Rights Reserved. */ import * as go from "../release/go"; // A custom Tool for shifting the end point of a Link to be anywhere along the edges of the port. /** * This constructor produces a tool for shifting the end of a link; * use it in a diagram.toolManager.mouseDownTools list: * <pre>myDiagram.toolManager.mouseDownTools.add(new LinkShiftingTool());</pre> * @constructor * @extends Tool * @class */ export class LinkShiftingTool extends go.Tool { // these are archetypes for the two shift handles, one at each end of the Link: /** @type {GraphObject} */ private _fromHandleArchetype: go.GraphObject; /** @type {GraphObject} */ private _toHandleArchetype: go.GraphObject; constructor() { super(); const h: go.Shape = new go.Shape(); const g: go.Shape = new go.Shape(); h.geometryString = "F1 M0 0 L8 0 M8 4 L0 4"; h.fill = null; h.stroke = "dodgerblue"; h.background = "lightblue"; h.cursor = "pointer"; h.segmentIndex = 0; h.segmentFraction = 1; h.segmentOrientation = go.Link.OrientAlong; g.geometryString = "F1 M0 0 L8 0 M8 4 L0 4"; g.fill = null; g.stroke = "dodgerblue"; g.background = "lightblue"; g.cursor = "pointer"; g.segmentIndex = -1; g.segmentFraction = 1; g.segmentOrientation = go.Link.OrientAlong; this._fromHandleArchetype = h; this._toHandleArchetype = g; } public readonly name: string = "LinkShifting"; // transient state /** @type {GraphObject} */ private _handle: go.GraphObject = null; /** @type {List} */ private _originalPoints: go.List<go.Point> = null; /** * @this {LinkShiftingTool} * @param {Part} part */ public updateAdornments(part: go.Part) { if (part === null || !(part instanceof go.Link)) return; // this tool only applies to Links var link: go.Link = part; // show handles if link is selected, remove them if no longer selected var category = "LinkShiftingFrom"; var adornment = null; if (link.isSelected && !this.diagram.isReadOnly) { var selelt = link.selectionObject; if (selelt !== null && link.actualBounds.isReal() && link.isVisible() && selelt.actualBounds.isReal() && selelt.isVisibleObject()) { var spot = (<any>link).computeSpot(true); if (spot.isSide() || spot.isSpot()) { adornment = link.findAdornment(category); if (adornment === null) { adornment = this.makeAdornment(selelt, false); adornment.category = category; link.addAdornment(category, adornment); } } } } if (adornment === null) link.removeAdornment(category); category = "LinkShiftingTo"; adornment = null; if (link.isSelected && !this.diagram.isReadOnly) { var selelt = link.selectionObject; if (selelt !== null && link.actualBounds.isReal() && link.isVisible() && selelt.actualBounds.isReal() && selelt.isVisibleObject()) { var spot = (<any>link).computeSpot(false); if (spot.isSide() || spot.isSpot()) { adornment = link.findAdornment(category); if (adornment === null) { adornment = this.makeAdornment(selelt, true); adornment.category = category; link.addAdornment(category, adornment); } } } } if (adornment === null) link.removeAdornment(category); }; /** * @this {LinkShiftingTool} * @param {GraphObject} selelt the {@link GraphObject} of the {@link Link} being shifted. * @param {boolean} toend * @return {Adornment} */ public makeAdornment(selelt: go.GraphObject, toend: boolean): go.Adornment { var adornment = new go.Adornment(); adornment.type = go.Panel.Link; var h = (toend ? this._toHandleArchetype : this._fromHandleArchetype); if (h !== null) { // add a single handle for shifting at one end adornment.add(h.copy()); } adornment.adornedObject = selelt; return adornment; }; /** * @this {LinkShiftingTool} * @return {boolean} */ public canStart(): boolean { if (!this.isEnabled) return false; var diagram = this.diagram; if (diagram === null || diagram.isReadOnly || diagram.isModelReadOnly) return false; if (!diagram.lastInput.left) return false; var h = this.findToolHandleAt(diagram.firstInput.documentPoint, "LinkShiftingFrom"); if (h === null) h = this.findToolHandleAt(diagram.firstInput.documentPoint, "LinkShiftingTo"); return (h !== null); } /** * @this {LinkShiftingTool} */ public doActivate() { var diagram = this.diagram; if (diagram === null) return; var h = this.findToolHandleAt(diagram.firstInput.documentPoint, "LinkShiftingFrom"); if (h === null) h = this.findToolHandleAt(diagram.firstInput.documentPoint, "LinkShiftingTo"); if (h === null) return; var ad = h.part as go.Adornment; var link = ad.adornedObject.part; if (!(link instanceof go.Link)) return; this._handle = h; this._originalPoints = link.points.copy(); this.startTransaction(this.name); diagram.isMouseCaptured = true; diagram.currentCursor = 'pointer'; this.isActive = true; }; /** * @this {LinkShiftingTool} */ public doDeactivate() { this.isActive = false; var diagram = this.diagram; if (diagram === null) return; diagram.isMouseCaptured = false; diagram.currentCursor = ''; this.stopTransaction(); }; /** * Clean up tool state. * @this {LinkShiftingTool} */ public doStop() { this._handle = null; this._originalPoints = null; }; /** * Clean up tool state. * @this {LinkShiftingTool} */ public doCancel() { var ad = this._handle.part as go.Adornment; var link = ad.adornedObject.part as go.Link; link.points = this._originalPoints; this.stopTool(); }; /** * @this {LinkShiftingTool} */ public doMouseMove() { if (this.isActive) { this.doReshape(this.diagram.lastInput.documentPoint); } }; /** * @this {LinkShiftingTool} */ public doMouseUp() { if (this.isActive) { this.doReshape(this.diagram.lastInput.documentPoint); this.transactionResult = this.name; } this.stopTool(); }; /** * @this {LinkShiftingTool} * @param {Point} pt */ public doReshape(pt: go.Point) { var ad = this._handle.part as go.Adornment; var link = ad.adornedObject.part as go.Link; var fromend = ad.category === "LinkShiftingFrom"; var port = null; if (fromend) { port = link.fromPort; } else { port = link.toPort; } var portb = new go.Rect(port.getDocumentPoint(go.Spot.TopLeft), port.getDocumentPoint(go.Spot.BottomRight)); // determine new connection point based on closest point to bounds of port property var x = portb.width > 0 ? (pt.x - portb.x) / portb.width : 0; var y = portb.height > 0 ? (pt.y - portb.y) / portb.height : 0; var sx = undefined; var sy = undefined; if (x <= 0) { sx = 0; if (y <= 0) { sy = 0; } else if (y >= 1) { sy = 1; } else { sy = y; } } else if (x >= 1) { sx = 1; if (y <= 0) { sy = 0; } else if (y >= 1) { sy = 1; } else { sy = y; } } else { if (y <= 0) { sx = x; sy = 0; } else if (y >= 1) { sx = x; sy = 1; } else { // in the middle if (x > y) { if (x > 1 - y) { sx = 1; // right side sy = y; } else { sx = x; sy = 0; // top side } } else { // y <= x if (x > 1 - y) { sx = x; sy = 1; // bottom side } else { sx = 0; // left side sy = y; } } } } if (sx !== undefined && sy !== undefined) { if (fromend) { link.fromSpot = new go.Spot(sx, sy); } else { link.toSpot = new go.Spot(sx, sy); } } }; }