UNPKG

@eclipse-glsp/client

Version:

A sprotty-based client for GLSP

399 lines 17.6 kB
"use strict"; 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); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.AlignElementsActionHandler = exports.AlignElementsAction = exports.SelectFunction = exports.Alignment = exports.ResizeElementsActionHandler = exports.LayoutElementsActionHandler = exports.ResizeElementsAction = exports.ReduceFunction = exports.ResizeDimension = void 0; /******************************************************************************** * Copyright (c) 2019-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 ********************************************************************************/ const sprotty_1 = require("@eclipse-glsp/sprotty"); const inversify_1 = require("inversify"); const selection_service_1 = require("../../base/selection-service"); const gmodel_util_1 = require("../../utils/gmodel-util"); const layout_utils_1 = require("../../utils/layout-utils"); const model_1 = require("../change-bounds/model"); /** * Used to specify the desired resize dimension for a {@link ResizeElementsCommand}. */ var ResizeDimension; (function (ResizeDimension) { ResizeDimension[ResizeDimension["Width"] = 0] = "Width"; ResizeDimension[ResizeDimension["Height"] = 1] = "Height"; ResizeDimension[ResizeDimension["Width_And_Height"] = 2] = "Width_And_Height"; })(ResizeDimension || (exports.ResizeDimension = ResizeDimension = {})); var ReduceFunction; (function (ReduceFunction) { /** * Returns the minimal value of the given numbers. * @param values Numbers to be evaluated. * @returns The reduced number. */ function min(...values) { return Math.min(...values); } ReduceFunction.min = min; /** * Returns the maximal value of the given numbers. * @param values Numbers to be evaluated. * @returns The reduced number. */ function max(...values) { return Math.max(...values); } ReduceFunction.max = max; /** * Computes the average of the given numbers. * @param values Numbers to be evaluated. */ function avg(...values) { return values.reduce((a, b) => a + b, 0) / values.length; } ReduceFunction.avg = avg; /** * Returns the last value of the given numbers. * @param values Numbers to be evaluated. * @returns The reduced number. */ function first(...values) { return values[0]; } ReduceFunction.first = first; /** * Returns the minimal value of the given numbers. * @param values Numbers to be evaluated. * @returns The reduced number. */ function last(...values) { return values[values.length - 1]; } ReduceFunction.last = last; /** * Returns the reduce function that corresponds to the given {@link ReduceFunctionType}. * @param type The reduce function kind. * @returns The corresponding reduce function. */ function get(type) { return ReduceFunction[type]; } ReduceFunction.get = get; })(ReduceFunction || (exports.ReduceFunction = ReduceFunction = {})); var ResizeElementsAction; (function (ResizeElementsAction) { ResizeElementsAction.KIND = 'resizeElementAction'; function is(object) { return (sprotty_1.Action.hasKind(object, ResizeElementsAction.KIND) && (0, sprotty_1.hasArrayProp)(object, 'elementIds') && (0, sprotty_1.hasNumberProp)(object, 'dimension') && (0, sprotty_1.hasStringProp)(object, 'reduceFunction')); } ResizeElementsAction.is = is; function create(options) { return { kind: ResizeElementsAction.KIND, dimension: ResizeDimension.Width, elementIds: [], ...options }; } ResizeElementsAction.create = create; })(ResizeElementsAction || (exports.ResizeElementsAction = ResizeElementsAction = {})); let LayoutElementsActionHandler = class LayoutElementsActionHandler { getSelectedElements(selection) { const index = this.selectionService.getModelRoot().index; const selectedElements = selection.elementIds.length > 0 ? selection.elementIds : this.selectionService.getSelectedElementIDs(); return (0, gmodel_util_1.getElements)(index, selectedElements, this.isActionElement); } dispatchAction(action) { this.actionDispatcher.dispatch(action); } dispatchActions(actions) { this.actionDispatcher.dispatchAll(actions); } }; exports.LayoutElementsActionHandler = LayoutElementsActionHandler; __decorate([ (0, inversify_1.inject)(sprotty_1.TYPES.IActionDispatcher), __metadata("design:type", Object) ], LayoutElementsActionHandler.prototype, "actionDispatcher", void 0); __decorate([ (0, inversify_1.inject)(selection_service_1.SelectionService), __metadata("design:type", selection_service_1.SelectionService) ], LayoutElementsActionHandler.prototype, "selectionService", void 0); __decorate([ (0, inversify_1.inject)(sprotty_1.TYPES.IMovementRestrictor), (0, inversify_1.optional)(), __metadata("design:type", Object) ], LayoutElementsActionHandler.prototype, "movementRestrictor", void 0); exports.LayoutElementsActionHandler = LayoutElementsActionHandler = __decorate([ (0, inversify_1.injectable)() ], LayoutElementsActionHandler); let ResizeElementsActionHandler = class ResizeElementsActionHandler extends LayoutElementsActionHandler { handle(action) { const elements = this.getSelectedElements(action); if (elements.length > 1) { const reduceFn = ReduceFunction.get(action.reduceFunction); switch (action.dimension) { case ResizeDimension.Width: return this.resizeWidth(elements, reduceFn); case ResizeDimension.Height: return this.resizeHeight(elements, reduceFn); case ResizeDimension.Width_And_Height: return this.resizeWidthAndHeight(elements, reduceFn); } } } resizeWidth(elements, reduceFn) { const targetWidth = reduceFn(...elements.map(element => element.bounds.width)); this.dispatchResizeActions(elements, (element, bounds) => { // resize around center (horizontal) const halfDiffWidth = 0.5 * (targetWidth - element.bounds.width); bounds.newPosition.x = element.bounds.x - halfDiffWidth; bounds.newSize.width = targetWidth; }); } resizeHeight(elements, reduceFn) { const targetHeight = reduceFn(...elements.map(element => element.bounds.height)); this.dispatchResizeActions(elements, (element, bounds) => { // resize around middle (vertical) const halfDiffHeight = 0.5 * (targetHeight - element.bounds.height); bounds.newPosition.y = element.bounds.y - halfDiffHeight; bounds.newSize.height = targetHeight; }); } resizeWidthAndHeight(elements, reduceFn) { const targetWidth = reduceFn(...elements.map(element => element.bounds.width)); const targetHeight = reduceFn(...elements.map(element => element.bounds.height)); const targetDimension = { width: targetWidth, height: targetHeight }; this.dispatchResizeActions(elements, (element, bounds) => { // resize around center and middle (horizontal & vertical) const difference = sprotty_1.Dimension.subtract(targetDimension, element.bounds); const center = sprotty_1.Dimension.center(difference); bounds.newPosition = sprotty_1.Point.subtract(element.bounds, center); bounds.newSize = targetDimension; }); } dispatchResizeActions(elements, change) { const elementAndBounds = []; // client- and server-side resize elements.forEach(element => { const elementChange = this.createElementAndBounds(element, change); if (elementChange) { // simply skip invalid changes elementAndBounds.push(elementChange); } }); this.dispatchActions([sprotty_1.SetBoundsAction.create(elementAndBounds), sprotty_1.ChangeBoundsOperation.create(elementAndBounds)]); } createElementAndBounds(element, change) { const bounds = { elementId: element.id, newPosition: { x: element.bounds.x, y: element.bounds.y }, newSize: { width: element.bounds.width, height: element.bounds.height } }; change(element, bounds); return (0, layout_utils_1.toValidElementAndBounds)(element, bounds, this.movementRestrictor); } isActionElement(element) { return (0, model_1.isResizable)(element); } }; exports.ResizeElementsActionHandler = ResizeElementsActionHandler; exports.ResizeElementsActionHandler = ResizeElementsActionHandler = __decorate([ (0, inversify_1.injectable)() ], ResizeElementsActionHandler); var Alignment; (function (Alignment) { Alignment[Alignment["Left"] = 0] = "Left"; Alignment[Alignment["Center"] = 1] = "Center"; Alignment[Alignment["Right"] = 2] = "Right"; Alignment[Alignment["Top"] = 3] = "Top"; Alignment[Alignment["Middle"] = 4] = "Middle"; Alignment[Alignment["Bottom"] = 5] = "Bottom"; })(Alignment || (exports.Alignment = Alignment = {})); var SelectFunction; (function (SelectFunction) { /** * Select all elements from the given set of elements. * @param elements The set of elements. * @returns All elements. */ function all(elements) { return elements; } SelectFunction.all = all; /** * Select the first element from a given set of elements. * @param elements The elements. * @returns An array containing the first element of the given elements. */ function first(elements) { return [elements[0]]; } SelectFunction.first = first; /** * Select the last element from a given set of elements. * @param elements The elements. * @returns An array containing the last element of the given elements. */ function last(elements) { return [elements[elements.length - 1]]; } SelectFunction.last = last; /** * Returns the select function that corresponds to the given {@link SelectFunctionType}. * @param type The select function type. * @returns The corresponding select function. */ function get(kind) { return SelectFunction[kind]; } SelectFunction.get = get; })(SelectFunction || (exports.SelectFunction = SelectFunction = {})); var AlignElementsAction; (function (AlignElementsAction) { AlignElementsAction.KIND = 'alignElements'; function is(object) { return (sprotty_1.Action.hasKind(object, AlignElementsAction.KIND) && (0, sprotty_1.hasArrayProp)(object, 'elementIds') && (0, sprotty_1.hasNumberProp)(object, 'alignment') && (0, sprotty_1.hasStringProp)(object, 'selectFunction')); } AlignElementsAction.is = is; function create(options = {}) { return { kind: AlignElementsAction.KIND, elementIds: [], alignment: Alignment.Left, selectFunction: 'all', ...options }; } AlignElementsAction.create = create; })(AlignElementsAction || (exports.AlignElementsAction = AlignElementsAction = {})); let AlignElementsActionHandler = class AlignElementsActionHandler extends LayoutElementsActionHandler { handle(action) { const elements = this.getSelectedElements(action); const selectFn = SelectFunction.get(action.selectFunction); const calculatedElements = selectFn(elements); if (elements.length > 1) { switch (action.alignment) { case Alignment.Left: return this.alignLeft(calculatedElements); case Alignment.Center: return this.alignCenter(calculatedElements); case Alignment.Right: return this.alignRight(calculatedElements); case Alignment.Top: return this.alignTop(calculatedElements); case Alignment.Middle: return this.alignMiddle(calculatedElements); case Alignment.Bottom: return this.alignBottom(calculatedElements); } } } alignLeft(elements) { const minX = elements.map(element => element.bounds.x).reduce((a, b) => Math.min(a, b)); this.dispatchAlignActions(elements, (_, move) => (move.toPosition.x = minX)); } alignCenter(elements) { const minX = elements.map(element => element.bounds.x).reduce((a, b) => Math.min(a, b)); const maxX = elements.map(element => element.bounds.x + element.bounds.width).reduce((a, b) => Math.max(a, b)); const diffX = maxX - minX; const centerX = minX + 0.5 * diffX; this.dispatchAlignActions(elements, (element, move) => (move.toPosition.x = centerX - 0.5 * element.bounds.width)); } alignRight(elements) { const maxX = elements.map(element => element.bounds.x + element.bounds.width).reduce((a, b) => Math.max(a, b)); this.dispatchAlignActions(elements, (element, move) => (move.toPosition.x = maxX - element.bounds.width)); } alignTop(elements) { const minY = elements.map(element => element.bounds.y).reduce((a, b) => Math.min(a, b)); this.dispatchAlignActions(elements, (_, move) => (move.toPosition.y = minY)); } alignMiddle(elements) { const minY = elements.map(element => element.bounds.y).reduce((a, b) => Math.min(a, b)); const maxY = elements.map(element => element.bounds.y + element.bounds.height).reduce((a, b) => Math.max(a, b)); const diffY = maxY - minY; const middleY = minY + 0.5 * diffY; this.dispatchAlignActions(elements, (element, move) => (move.toPosition.y = middleY - 0.5 * element.bounds.height)); } alignBottom(elements) { const maxY = elements.map(element => element.bounds.y + element.bounds.height).reduce((a, b) => Math.max(a, b)); this.dispatchAlignActions(elements, (element, move) => (move.toPosition.y = maxY - element.bounds.height)); } dispatchAlignActions(elements, change) { const moves = []; // client-side move const elementAndBounds = []; // server-side move elements.forEach(element => { const move = this.createElementMove(element, change); if (move) { // simply skip invalid changes moves.push(move); const elementAndBound = this.createElementAndBounds(element, move); elementAndBounds.push(elementAndBound); } }); this.dispatchActions([sprotty_1.MoveAction.create(moves), sprotty_1.ChangeBoundsOperation.create(elementAndBounds)]); } createElementMove(element, change) { const move = { elementId: element.id, fromPosition: { x: element.bounds.x, y: element.bounds.y }, toPosition: { x: element.bounds.x, y: element.bounds.y } }; change(element, move); return (0, layout_utils_1.toValidElementMove)(element, move, this.movementRestrictor); } createElementAndBounds(element, move) { return { elementId: element.id, newPosition: { x: move.toPosition.x, y: move.toPosition.y }, newSize: { width: element.bounds.width, height: element.bounds.height } }; } isActionElement(element) { return (0, model_1.isBoundsAwareMoveable)(element); } }; exports.AlignElementsActionHandler = AlignElementsActionHandler; exports.AlignElementsActionHandler = AlignElementsActionHandler = __decorate([ (0, inversify_1.injectable)() ], AlignElementsActionHandler); //# sourceMappingURL=layout-elements-action.js.map