@eclipse-glsp/client
Version:
A sprotty-based client for GLSP
299 lines • 19.3 kB
JavaScript
"use strict";
/********************************************************************************
* Copyright (c) 2023-2025 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.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;
exports.removeHelperLines = removeHelperLines;
exports.removeSelectionBounds = removeSelectionBounds;
exports.boundsInViewport = boundsInViewport;
const sprotty_1 = require("@eclipse-glsp/sprotty");
const inversify_1 = require("inversify");
const lodash_1 = require("lodash");
require("../../../css/helper-lines.css");
const editor_context_service_1 = require("../../base/editor-context-service");
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 = this.editorContext.viewport;
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;
__decorate([
(0, inversify_1.inject)(editor_context_service_1.EditorContextService),
__metadata("design:type", editor_context_service_1.EditorContextService)
], DrawHelperLinesFeedbackCommand.prototype, "editorContext", void 0);
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));
}
function removeSelectionBounds(root) {
(0, gmodel_util_1.forEachElement)(root.index, model_1.isSelectionBounds, line => root.remove(line));
}
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;
}
}
//# sourceMappingURL=helper-line-feedback.js.map