UNPKG

@eclipse-glsp/client

Version:

A sprotty-based client for GLSP

330 lines 17 kB
"use strict"; /******************************************************************************** * Copyright (c) 2024 EclipseSource and others. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-2.0. * * This Source Code may also be made available under the following Secondary * Licenses when the conditions for such availability set forth in the Eclipse * Public License v. 2.0 are satisfied: GNU General Public License, version 2 * with the GNU Classpath Exception which is available at * https://www.gnu.org/software/classpath/license.html. * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ Object.defineProperty(exports, "__esModule", { value: true }); exports.MoveableRoutingHandle = exports.MoveableResizeHandle = exports.ChangeBoundsTracker = exports.TrackedElementResize = exports.DEFAULT_RESIZE_OPTIONS = exports.TrackedMove = exports.TrackedElementMove = exports.DEFAULT_MOVE_OPTIONS = void 0; const sprotty_1 = require("@eclipse-glsp/sprotty"); const gmodel_util_1 = require("../../../utils/gmodel-util"); const model_1 = require("../../change-bounds/model"); const tracker_1 = require("../../change-bounds/tracker"); exports.DEFAULT_MOVE_OPTIONS = { snap: true, restrict: true, validate: true, skipStatic: true, skipInvalid: false }; var TrackedElementMove; (function (TrackedElementMove) { function is(obj) { return ((0, sprotty_1.hasObjectProp)(obj, 'element') && (0, sprotty_1.hasObjectProp)(obj, 'fromPosition') && (0, sprotty_1.hasObjectProp)(obj, 'toPosition') && (0, sprotty_1.hasBooleanProp)(obj, 'valid')); } TrackedElementMove.is = is; })(TrackedElementMove || (exports.TrackedElementMove = TrackedElementMove = {})); var TrackedMove; (function (TrackedMove) { function is(obj) { return sprotty_1.Movement.is(obj) && (0, sprotty_1.hasBooleanProp)(obj, 'valid'); } TrackedMove.is = is; })(TrackedMove || (exports.TrackedMove = TrackedMove = {})); exports.DEFAULT_RESIZE_OPTIONS = { snap: true, restrict: true, validate: true, symmetric: true, constrainResize: true, skipStatic: true, skipInvalidSize: false, skipInvalidMove: false }; var TrackedElementResize; (function (TrackedElementResize) { function is(obj) { return ((0, sprotty_1.isBoundsAware)(obj.element) && (0, sprotty_1.hasObjectProp)(obj, 'fromBounds') && (0, sprotty_1.hasObjectProp)(obj, 'toBounds') && (0, sprotty_1.hasObjectProp)(obj, 'valid')); } TrackedElementResize.is = is; })(TrackedElementResize || (exports.TrackedElementResize = TrackedElementResize = {})); class ChangeBoundsTracker { constructor(manager) { this.manager = manager; this.diagramMovement = new tracker_1.DiagramMovementCalculator(manager.positionTracker); } startTracking() { this.diagramMovement.init(); return this; } updateTrackingPosition(param) { const update = TrackedMove.is(param) ? sprotty_1.Vector.max(...param.elementMoves.map(move => move.moveVector)) : param; this.diagramMovement.updatePosition(update); } isTracking() { return this.diagramMovement.hasPosition; } stopTracking() { this.diagramMovement.dispose(); return this; } // // MOVE // moveElements(elements, opts) { const options = this.resolveMoveOptions(opts); const update = this.calculateDiagramMovement(); const move = { ...update, elementMoves: [], valid: true, options }; if (sprotty_1.Vector.isZero(update.vector) && options.skipStatic) { // no movement detected so elements won't be moved, exit early return move; } // calculate move for each element const elementsToMove = this.getMoveableElements(elements, options); for (const element of elementsToMove) { const elementMove = this.calculateElementMove(element, update.vector, options); if (!this.skipElementMove(elementMove, options)) { move.elementMoves.push(elementMove); move.valid && (move.valid = elementMove.valid); } } return move; } resolveMoveOptions(opts) { var _a, _b; return { ...exports.DEFAULT_MOVE_OPTIONS, ...opts, snap: this.manager.usePositionSnap((_a = opts === null || opts === void 0 ? void 0 : opts.snap) !== null && _a !== void 0 ? _a : exports.DEFAULT_MOVE_OPTIONS.snap), restrict: this.manager.useMovementRestriction((_b = opts === null || opts === void 0 ? void 0 : opts.restrict) !== null && _b !== void 0 ? _b : exports.DEFAULT_MOVE_OPTIONS.restrict) }; } calculateDiagramMovement() { return this.diagramMovement.calculateMoveToCurrent(); } getMoveableElements(elements, options) { var _a; return Array.isArray(elements) ? elements : (0, gmodel_util_1.getElements)(elements.ctx.index, elements.elementIDs, (_a = elements.guard) !== null && _a !== void 0 ? _a : sprotty_1.isMoveable); } skipElementMove(elementMove, options) { return (options.skipInvalid && !elementMove.valid) || (options.skipStatic && sprotty_1.Vector.isZero(elementMove.moveVector)); } calculateElementMove(element, vector, options) { const fromPosition = element.position; const toPosition = sprotty_1.Point.add(fromPosition, vector); const move = { element, fromPosition, toPosition, valid: true, moveVector: vector, sourceVector: vector }; if (options.snap) { move.toPosition = this.snapPosition(move, options); } if (options.restrict) { move.toPosition = this.restrictMovement(move, options); } if (options.validate) { move.valid = this.validateElementMove(move, options); } move.moveVector = sprotty_1.Point.vector(move.fromPosition, move.toPosition); return move; } snapPosition(elementMove, opts) { return this.manager.snapPosition(elementMove.element, elementMove.toPosition); } restrictMovement(elementMove, opts) { const movement = sprotty_1.Point.move(elementMove.fromPosition, elementMove.toPosition); return this.manager.restrictMovement(elementMove.element, movement).to; } validateElementMove(elementMove, opts) { return this.manager.hasValidPosition(elementMove.element, elementMove.toPosition); } // // RESIZE // resizeElements(handle, opts) { const options = this.resolveResizeOptions(opts); const update = this.calculateDiagramMovement(); const handleMove = this.calculateHandleMove(new MoveableResizeHandle(handle), update.vector, options); const resize = { ...update, valid: { move: true, size: true }, options, handleMove, elementResizes: [] }; if (sprotty_1.Vector.isZero(handleMove.moveVector) && options.skipStatic) { // no movement detected so elements won't be moved, exit early return resize; } // calculate resize for each element (typically only one element is resized at a time but customizations are possible) const elementsToResize = this.getResizableElements(handle, options); for (const element of elementsToResize) { const elementResize = this.calculateElementResize(element, handleMove, options); if (!this.skipElementResize(elementResize, options)) { resize.elementResizes.push(elementResize); resize.valid.move = resize.valid.move && elementResize.valid.move; resize.valid.size = resize.valid.size && elementResize.valid.size; } } return resize; } resolveResizeOptions(opts) { var _a, _b, _c; return { ...exports.DEFAULT_RESIZE_OPTIONS, ...opts, snap: this.manager.usePositionSnap((_a = opts === null || opts === void 0 ? void 0 : opts.snap) !== null && _a !== void 0 ? _a : exports.DEFAULT_RESIZE_OPTIONS.snap), restrict: this.manager.useMovementRestriction((_b = opts === null || opts === void 0 ? void 0 : opts.restrict) !== null && _b !== void 0 ? _b : exports.DEFAULT_RESIZE_OPTIONS.restrict), symmetric: this.manager.useSymmetricResize((_c = opts === null || opts === void 0 ? void 0 : opts.symmetric) !== null && _c !== void 0 ? _c : exports.DEFAULT_RESIZE_OPTIONS.symmetric) }; } calculateHandleMove(handle, diagramMovement, opts) { const moveOptions = this.resolveMoveOptions({ ...opts, validate: false }); return this.calculateElementMove(handle, diagramMovement, moveOptions); } getResizableElements(handle, options) { return [handle.parent]; } skipElementResize(elementResize, options) { return ((options.skipInvalidMove && !elementResize.valid.move) || (options.skipInvalidSize && !elementResize.valid.size) || (options.skipStatic && sprotty_1.Dimension.equals(elementResize.fromBounds, elementResize.toBounds))); } calculateElementResize(element, handleMove, options) { const fromBounds = element.bounds; const toBounds = this.calculateElementBounds(element, handleMove, options); const resize = { element, fromBounds, toBounds, valid: { size: true, move: true } }; if (options.validate) { resize.valid.size = this.manager.hasValidSize(resize.element, resize.toBounds); resize.valid.move = handleMove.valid && this.manager.hasValidPosition(resize.element, resize.toBounds); } return resize; } calculateElementBounds(element, handleMove, options) { let toBounds = this.calculateBounds(element.bounds, handleMove); if (options.symmetric) { const symmetricHandleMove = this.calculateSymmetricHandleMove(handleMove, options); toBounds = this.calculateBounds(toBounds, symmetricHandleMove); } if (!options.constrainResize || this.manager.hasValidSize(element, toBounds)) { return toBounds; } // we need to adjust to the minimum size but it is not enough to simply set the size // we need to make sure that the element is still at the expected position // we therefore constrain the movement vector to actually avoid going below the minimum size const minimum = this.manager.getMinimumSize(element); handleMove.moveVector = this.constrainResizeVector(element.bounds, handleMove, minimum); if (options.symmetric) { // if we have symmetric resize we want to distribute the constrained movement vector to both sides // but only for the dimension that was actually resized beyond the minimum handleMove.moveVector.x = element.bounds.width > minimum.width ? handleMove.moveVector.x / 2 : handleMove.moveVector.x; handleMove.moveVector.y = element.bounds.height > minimum.height ? handleMove.moveVector.y / 2 : handleMove.moveVector.y; } toBounds = this.calculateBounds(element.bounds, handleMove); if (options.symmetric) { // since we already distributed the available movement vector, we do not want to snap the symmetric handle move const symmetricHandleMove = this.calculateSymmetricHandleMove(handleMove, { ...options, snap: false }); toBounds = this.calculateBounds(toBounds, symmetricHandleMove); } return toBounds; } calculateSymmetricHandleMove(handleMove, options) { const moveOptions = this.resolveMoveOptions({ ...options, validate: false, restrict: false }); return this.calculateElementMove(handleMove.element.opposite(), sprotty_1.Vector.reverse(handleMove.moveVector), moveOptions); } calculateBounds(src, handleMove) { if (!handleMove || sprotty_1.Vector.isZero(handleMove.moveVector)) { return src; } return this.doCalculateBounds(src, handleMove.moveVector, handleMove.element.location); } doCalculateBounds(src, vector, location) { switch (location) { case model_1.ResizeHandleLocation.TopLeft: return { x: src.x + vector.x, y: src.y + vector.y, width: src.width - vector.x, height: src.height - vector.y }; case model_1.ResizeHandleLocation.Top: return { ...src, y: src.y + vector.y, height: src.height - vector.y }; case model_1.ResizeHandleLocation.TopRight: return { ...src, y: src.y + vector.y, width: src.width + vector.x, height: src.height - vector.y }; case model_1.ResizeHandleLocation.Right: return { ...src, width: src.width + vector.x }; case model_1.ResizeHandleLocation.BottomRight: return { ...src, width: src.width + vector.x, height: src.height + vector.y }; case model_1.ResizeHandleLocation.Bottom: return { ...src, height: src.height + vector.y }; case model_1.ResizeHandleLocation.BottomLeft: return { ...src, x: src.x + vector.x, width: src.width - vector.x, height: src.height + vector.y }; case model_1.ResizeHandleLocation.Left: return { ...src, x: src.x + vector.x, width: src.width - vector.x }; } } constrainResizeVector(src, handleMove, minimum) { const vector = handleMove.moveVector; switch (handleMove.element.location) { case model_1.ResizeHandleLocation.TopLeft: vector.x = src.width - vector.x < minimum.width ? src.width - minimum.width : vector.x; vector.y = src.height - vector.y < minimum.height ? src.height - minimum.height : vector.y; break; case model_1.ResizeHandleLocation.Top: vector.y = src.height - vector.y < minimum.height ? src.height - minimum.height : vector.y; break; case model_1.ResizeHandleLocation.TopRight: vector.x = src.width + vector.x < minimum.width ? minimum.width - src.width : vector.x; vector.y = src.height - vector.y < minimum.height ? src.height - minimum.height : vector.y; break; case model_1.ResizeHandleLocation.Right: vector.x = src.width + vector.x < minimum.width ? minimum.width - src.width : vector.x; break; case model_1.ResizeHandleLocation.BottomRight: vector.x = src.width + vector.x < minimum.width ? minimum.width - src.width : vector.x; vector.y = src.height + vector.y < minimum.height ? minimum.height - src.height : vector.y; break; case model_1.ResizeHandleLocation.Bottom: vector.y = src.height + vector.y < minimum.height ? minimum.height - src.height : vector.y; break; case model_1.ResizeHandleLocation.BottomLeft: vector.x = src.width - vector.x < minimum.width ? src.width - minimum.width : vector.x; vector.y = src.height + vector.y < minimum.height ? minimum.height - src.height : vector.y; break; case model_1.ResizeHandleLocation.Left: vector.x = src.width - vector.x < minimum.width ? src.width - minimum.width : vector.x; break; } return vector; } dispose() { this.stopTracking(); } } exports.ChangeBoundsTracker = ChangeBoundsTracker; class MoveableResizeHandle extends model_1.GResizeHandle { constructor(handle, location = handle.location, position = model_1.GResizeHandle.getHandlePosition(handle.parent, location)) { super(location, handle.type, handle.hoverFeedback); this.handle = handle; this.location = location; this.position = position; this.id = handle.id; // this only acts as a wrapper so we do not actually add this to the parent but still want the parent reference this.parent = handle.parent; } opposite() { return new MoveableResizeHandle(this.handle, model_1.ResizeHandleLocation.opposite(this.location)); } } exports.MoveableResizeHandle = MoveableResizeHandle; class MoveableRoutingHandle extends sprotty_1.GRoutingHandle { constructor(handle, position) { super(); this.handle = handle; this.position = position; this.id = handle.id; // this only acts as a wrapper so we do not actually add this to the parent but still want the parent reference this.parent = handle.parent; } } exports.MoveableRoutingHandle = MoveableRoutingHandle; //# sourceMappingURL=change-bounds-tracker.js.map