UNPKG

@eclipse-glsp/client

Version:

A sprotty-based client for GLSP

294 lines 19.1 kB
"use strict"; /******************************************************************************** * Copyright (c) 2023-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 ********************************************************************************/ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.boundsInViewport = exports.removeSelectionBounds = exports.removeHelperLines = exports.RemoveHelperLinesFeedbackCommand = exports.RemoveHelperLinesFeedbackAction = exports.DrawHelperLinesFeedbackCommand = exports.DrawHelperLinesFeedbackAction = exports.DEFAULT_DEBUG = exports.DEFAULT_ALIGNABLE_ELEMENT_FILTER = exports.DEFAULT_EPSILON = exports.DEFAULT_VIEWPORT_LINES = exports.DEFAULT_ELEMENT_LINES = exports.ALL_VIEWPORT_LINE_TYPES = exports.ALL_ELEMENT_LINE_TYPES = exports.isTopLevelBoundsAwareElement = void 0; const sprotty_1 = require("@eclipse-glsp/sprotty"); const inversify_1 = require("inversify"); const lodash_1 = require("lodash"); require("../../../css/helper-lines.css"); const feedback_command_1 = require("../../base/feedback/feedback-command"); const gmodel_util_1 = require("../../utils/gmodel-util"); const viewpoint_util_1 = require("../../utils/viewpoint-util"); const dangling_edge_feedback_1 = require("../tools/edge-creation/dangling-edge-feedback"); const model_1 = require("./model"); const isTopLevelBoundsAwareElement = element => (0, gmodel_util_1.findTopLevelElementByFeature)(element, sprotty_1.isBoundsAware, sprotty_1.isViewport) === element; exports.isTopLevelBoundsAwareElement = isTopLevelBoundsAwareElement; exports.ALL_ELEMENT_LINE_TYPES = Object.values(model_1.HelperLineType); exports.ALL_VIEWPORT_LINE_TYPES = [model_1.HelperLineType.Center, model_1.HelperLineType.Middle]; exports.DEFAULT_ELEMENT_LINES = exports.ALL_ELEMENT_LINE_TYPES; exports.DEFAULT_VIEWPORT_LINES = exports.ALL_VIEWPORT_LINE_TYPES; exports.DEFAULT_EPSILON = 1; const DEFAULT_ALIGNABLE_ELEMENT_FILTER = (element) => (0, gmodel_util_1.isVisibleOnCanvas)(element) && !(0, gmodel_util_1.isRoutable)(element) && !(element instanceof sprotty_1.GLabel) && !(element.id === (0, dangling_edge_feedback_1.feedbackEdgeId)(element.root)) && !(element.id === (0, dangling_edge_feedback_1.feedbackEdgeEndId)(element.root)) && !(0, sprotty_1.isDecoration)(element); exports.DEFAULT_ALIGNABLE_ELEMENT_FILTER = DEFAULT_ALIGNABLE_ELEMENT_FILTER; exports.DEFAULT_DEBUG = false; var DrawHelperLinesFeedbackAction; (function (DrawHelperLinesFeedbackAction) { DrawHelperLinesFeedbackAction.KIND = 'drawHelperLines'; function create(options) { return { kind: DrawHelperLinesFeedbackAction.KIND, ...options }; } DrawHelperLinesFeedbackAction.create = create; })(DrawHelperLinesFeedbackAction || (exports.DrawHelperLinesFeedbackAction = DrawHelperLinesFeedbackAction = {})); let DrawHelperLinesFeedbackCommand = class DrawHelperLinesFeedbackCommand extends feedback_command_1.FeedbackCommand { constructor(action, logger) { var _a, _b, _c, _d, _e; super(); this.logger = logger; this.elementIds = action.elementIds; this.elementLines = (_a = action.elementLines) !== null && _a !== void 0 ? _a : exports.DEFAULT_ELEMENT_LINES; this.viewportLines = (_b = action.viewportLines) !== null && _b !== void 0 ? _b : exports.DEFAULT_VIEWPORT_LINES; this.alignmentEpsilon = (_c = action.alignmentEpsilon) !== null && _c !== void 0 ? _c : exports.DEFAULT_EPSILON; this.alignableElementFilter = (_d = action.alignmentElementFilter) !== null && _d !== void 0 ? _d : exports.DEFAULT_ALIGNABLE_ELEMENT_FILTER; this.isAlignableElementPredicate = this.isAlignableElement.bind(this); this.debug = (_e = action.debug) !== null && _e !== void 0 ? _e : exports.DEFAULT_DEBUG; } execute(context) { removeHelperLines(context.root); removeSelectionBounds(context.root); const alignableElements = (0, gmodel_util_1.getMatchingElements)(context.root.index, this.isAlignableElementPredicate); this.log('All alignable elements: ', alignableElements); const [referenceElements, elements] = (0, lodash_1.partition)(alignableElements, element => this.elementIds.includes(element.id)); this.log('Split alignable elements into reference elements and other elements: ', referenceElements, elements); if (referenceElements.length === 0) { this.log('--> No helper lines as we do not have any reference elements.'); return context.root; } const referenceBounds = this.calcReferenceBounds(referenceElements); this.log('Bounds encompasing all reference elements: ', referenceBounds); const helperLines = this.calcHelperLines(elements, referenceBounds, context); if (referenceElements.length > 1) { context.root.add(new model_1.SelectionBounds(referenceBounds)); this.log('Render selection bounds for more than one reference element:', referenceBounds); } helperLines.forEach(helperLine => context.root.add(helperLine)); if (helperLines.length > 0) { this.log(`--> Add ${helperLines.length} helper lines to root:`, helperLines); } else { this.log('--> Add no helper lines to root.'); } return context.root; } isAlignableElement(element) { return (0, sprotty_1.isBoundsAware)(element) && this.alignableElementFilter(element, this.elementIds); } calcReferenceBounds(referenceElements) { return referenceElements.map(element => this.calcBounds(element)).reduce(sprotty_1.Bounds.combine, sprotty_1.Bounds.EMPTY); } calcBounds(element) { return (0, viewpoint_util_1.toAbsoluteBounds)(element); } calcHelperLines(elements, bounds, context) { const helperLines = []; const viewport = (0, sprotty_1.findParentByFeature)(context.root, sprotty_1.isViewport); if (viewport) { helperLines.push(...this.calcHelperLinesForViewport(viewport, bounds, this.viewportLines)); } elements .flatMap(element => this.calcHelperLinesForElement(element, bounds, this.elementLines)) .forEach(line => helperLines.push(line)); return helperLines; } calcHelperLinesForViewport(root, bounds, lineTypes) { const helperLines = []; this.log('Find helperlines for viewport:', root); const viewportBounds = (0, viewpoint_util_1.getViewportBounds)(root, root.canvasBounds); if (lineTypes.includes(model_1.HelperLineType.Center) && this.isAligned(sprotty_1.Bounds.centerX, viewportBounds, bounds, 2)) { helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.topCenter(viewportBounds), sprotty_1.Bounds.bottomCenter(viewportBounds), model_1.HelperLineType.Center)); this.log('- Reference bounds center align with viewport.', viewportBounds); } if (lineTypes.includes(model_1.HelperLineType.Middle) && this.isAligned(sprotty_1.Bounds.middle, viewportBounds, bounds, 2)) { helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.middleLeft(viewportBounds), sprotty_1.Bounds.middleRight(viewportBounds), model_1.HelperLineType.Middle)); this.log('- Reference bounds middle align with viewport.', viewportBounds); } if (helperLines.length > 0) { this.log(`--> Add ${helperLines.length} helperlines for viewport:`, helperLines); } return helperLines; } calcHelperLinesForElement(element, bounds, lineTypes) { this.log('Find helperlines for element:', element); return this.calcHelperLinesForBounds(this.calcBounds(element), bounds, lineTypes); } calcHelperLinesForBounds(elementBounds, bounds, lineTypes) { const helperLines = []; if (lineTypes.includes(model_1.HelperLineType.Left) && this.isAligned(sprotty_1.Bounds.left, elementBounds, bounds, this.alignmentEpsilon)) { const [above, below] = sprotty_1.Bounds.sortBy(sprotty_1.Bounds.top, elementBounds, bounds); // higher top-value ==> lower helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.bottomLeft(below), sprotty_1.Bounds.topLeft(above), model_1.HelperLineType.Left)); this.log('- Reference bounds left align with element', elementBounds); } if (lineTypes.includes(model_1.HelperLineType.Center) && this.isAligned(sprotty_1.Bounds.centerX, elementBounds, bounds, this.alignmentEpsilon)) { const [above, below] = sprotty_1.Bounds.sortBy(sprotty_1.Bounds.top, elementBounds, bounds); // higher top-value ==> lower helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.topCenter(above), sprotty_1.Bounds.bottomCenter(below), model_1.HelperLineType.Center)); this.log('- Reference bounds center align with element', elementBounds); } if (lineTypes.includes(model_1.HelperLineType.Right) && this.isAligned(sprotty_1.Bounds.right, elementBounds, bounds, this.alignmentEpsilon)) { const [above, below] = sprotty_1.Bounds.sortBy(sprotty_1.Bounds.top, elementBounds, bounds); // higher top-value ==> lower helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.bottomRight(below), sprotty_1.Bounds.topRight(above), model_1.HelperLineType.Right)); this.log('- Reference bounds right align with element', elementBounds); } if (lineTypes.includes(model_1.HelperLineType.Bottom) && this.isAligned(sprotty_1.Bounds.bottom, elementBounds, bounds, this.alignmentEpsilon)) { const [before, after] = sprotty_1.Bounds.sortBy(sprotty_1.Bounds.left, elementBounds, bounds); // higher left-value ==> more to the right helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.bottomLeft(before), sprotty_1.Bounds.bottomRight(after), model_1.HelperLineType.Bottom)); this.log('- Reference bounds bottom align with element', elementBounds); } if (lineTypes.includes(model_1.HelperLineType.Middle) && this.isAligned(sprotty_1.Bounds.centerY, elementBounds, bounds, this.alignmentEpsilon)) { const [before, after] = sprotty_1.Bounds.sortBy(sprotty_1.Bounds.left, elementBounds, bounds); // higher left-value ==> more to the right helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.middleLeft(before), sprotty_1.Bounds.middleRight(after), model_1.HelperLineType.Middle)); this.log('- Reference bounds middle align with element', elementBounds); } if (lineTypes.includes(model_1.HelperLineType.Top) && this.isAligned(sprotty_1.Bounds.top, elementBounds, bounds, this.alignmentEpsilon)) { const [before, after] = sprotty_1.Bounds.sortBy(sprotty_1.Bounds.left, elementBounds, bounds); // higher left-value ==> more to the right helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.topLeft(before), sprotty_1.Bounds.topRight(after), model_1.HelperLineType.Top)); this.log('- Reference bounds top align with element', elementBounds); } if (lineTypes.includes(model_1.HelperLineType.LeftRight) && this.isMatch(sprotty_1.Bounds.left(elementBounds), sprotty_1.Bounds.right(bounds), this.alignmentEpsilon)) { if (sprotty_1.Bounds.isAbove(bounds, elementBounds)) { helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.bottomLeft(elementBounds), sprotty_1.Bounds.topRight(bounds), model_1.HelperLineType.RightLeft)); this.log('- Reference bounds right aligns with element left', elementBounds); } else { helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.topLeft(elementBounds), sprotty_1.Bounds.bottomRight(bounds), model_1.HelperLineType.RightLeft)); this.log('- Reference bounds right aligns with element left', elementBounds); } } if (lineTypes.includes(model_1.HelperLineType.LeftRight) && this.isMatch(sprotty_1.Bounds.right(elementBounds), sprotty_1.Bounds.left(bounds), this.alignmentEpsilon)) { if (sprotty_1.Bounds.isAbove(bounds, elementBounds)) { helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.bottomRight(elementBounds), sprotty_1.Bounds.topLeft(bounds), model_1.HelperLineType.LeftRight)); this.log('- Reference bounds left aligns with element right', elementBounds); } else { helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.topRight(elementBounds), sprotty_1.Bounds.bottomLeft(bounds), model_1.HelperLineType.LeftRight)); this.log('- Reference bounds left aligns with element right', elementBounds); } } if (lineTypes.includes(model_1.HelperLineType.TopBottom) && this.isMatch(sprotty_1.Bounds.top(elementBounds), sprotty_1.Bounds.bottom(bounds), this.alignmentEpsilon)) { if (sprotty_1.Bounds.isBefore(bounds, elementBounds)) { helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.topRight(elementBounds), sprotty_1.Bounds.bottomLeft(bounds), model_1.HelperLineType.BottomTop)); this.log('- Reference bounds bottom aligns with element top', elementBounds); } else { helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.topLeft(elementBounds), sprotty_1.Bounds.bottomRight(bounds), model_1.HelperLineType.BottomTop)); this.log('- Reference bounds bottom aligns with element top', elementBounds); } } if (lineTypes.includes(model_1.HelperLineType.TopBottom) && this.isMatch(sprotty_1.Bounds.bottom(elementBounds), sprotty_1.Bounds.top(bounds), this.alignmentEpsilon)) { if (sprotty_1.Bounds.isBefore(bounds, elementBounds)) { helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.bottomRight(elementBounds), sprotty_1.Bounds.topLeft(bounds), model_1.HelperLineType.TopBottom)); this.log('- Reference bounds top aligns with element bottom', elementBounds); } else { helperLines.push(new model_1.HelperLine(sprotty_1.Bounds.bottomLeft(elementBounds), sprotty_1.Bounds.topRight(bounds), model_1.HelperLineType.TopBottom)); this.log('- Reference bounds top aligns with element bottom', elementBounds); } } if (helperLines.length > 0) { this.log(`--> Add ${helperLines.length} helperlines for element:`, helperLines); } return helperLines; } isAligned(coordinate, leftBounds, rightBounds, epsilon) { return this.isMatch(coordinate(leftBounds), coordinate(rightBounds), epsilon); } isMatch(leftCoordinate, rightCoordinate, epsilon) { return (0, sprotty_1.equalUpTo)(leftCoordinate, rightCoordinate, epsilon); } log(message, ...params) { if (this.debug) { this.logger.log(this, message, params); } } }; exports.DrawHelperLinesFeedbackCommand = DrawHelperLinesFeedbackCommand; DrawHelperLinesFeedbackCommand.KIND = DrawHelperLinesFeedbackAction.KIND; exports.DrawHelperLinesFeedbackCommand = DrawHelperLinesFeedbackCommand = __decorate([ (0, inversify_1.injectable)(), __param(0, (0, inversify_1.inject)(sprotty_1.TYPES.Action)), __param(1, (0, inversify_1.inject)(sprotty_1.TYPES.ILogger)), __metadata("design:paramtypes", [Object, Object]) ], DrawHelperLinesFeedbackCommand); var RemoveHelperLinesFeedbackAction; (function (RemoveHelperLinesFeedbackAction) { RemoveHelperLinesFeedbackAction.KIND = 'removeHelperLines'; function create(options = {}) { return { kind: RemoveHelperLinesFeedbackAction.KIND, ...options }; } RemoveHelperLinesFeedbackAction.create = create; })(RemoveHelperLinesFeedbackAction || (exports.RemoveHelperLinesFeedbackAction = RemoveHelperLinesFeedbackAction = {})); let RemoveHelperLinesFeedbackCommand = class RemoveHelperLinesFeedbackCommand extends feedback_command_1.FeedbackCommand { constructor(action) { super(); this.action = action; } execute(context) { removeHelperLines(context.root); removeSelectionBounds(context.root); return context.root; } }; exports.RemoveHelperLinesFeedbackCommand = RemoveHelperLinesFeedbackCommand; RemoveHelperLinesFeedbackCommand.KIND = RemoveHelperLinesFeedbackAction.KIND; exports.RemoveHelperLinesFeedbackCommand = RemoveHelperLinesFeedbackCommand = __decorate([ (0, inversify_1.injectable)(), __param(0, (0, inversify_1.inject)(sprotty_1.TYPES.Action)), __metadata("design:paramtypes", [Object]) ], RemoveHelperLinesFeedbackCommand); function removeHelperLines(root) { (0, gmodel_util_1.forEachElement)(root.index, model_1.isHelperLine, line => root.remove(line)); } exports.removeHelperLines = removeHelperLines; function removeSelectionBounds(root) { (0, gmodel_util_1.forEachElement)(root.index, model_1.isSelectionBounds, line => root.remove(line)); } exports.removeSelectionBounds = removeSelectionBounds; function boundsInViewport(element, bounds) { if (element instanceof sprotty_1.GChildElement && !(0, sprotty_1.isViewport)(element.parent)) { return boundsInViewport(element.parent, element.parent.localToParent(bounds)); } else { return bounds; } } exports.boundsInViewport = boundsInViewport; //# sourceMappingURL=helper-line-feedback.js.map