@eclipse-glsp/client
Version:
A sprotty-based client for GLSP
399 lines • 17.6 kB
JavaScript
;
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