UNPKG

@eclipse-glsp/client

Version:

A sprotty-based client for GLSP

274 lines 13.2 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); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SelectFeedbackAction = exports.SelectAllCommand = exports.SelectCommand = exports.SelectionService = exports.ISelectionListener = 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 gmodel_util_1 = require("../utils/gmodel-util"); var ISelectionListener; (function (ISelectionListener) { function is(object) { return sprotty_1.AnyObject.is(object) && (0, sprotty_1.hasFunctionProp)(object, 'selectionChanged'); } ISelectionListener.is = is; })(ISelectionListener || (exports.ISelectionListener = ISelectionListener = {})); let SelectionService = class SelectionService { constructor() { this.selectedElementIDs = new Set(); this.toDispose = new sprotty_1.DisposableCollection(); this.onSelectionChangedEmitter = new sprotty_1.Emitter(); } initialize() { this.toDispose.push(this.onSelectionChangedEmitter); } preLoadDiagram() { this.lazyInjector.getAll(sprotty_1.TYPES.ISelectionListener).forEach(listener => this.addListener(listener)); } dispose() { this.toDispose.dispose(); } get onSelectionChanged() { return this.onSelectionChangedEmitter.event; } addListener(listener) { return this.onSelectionChanged(change => listener.selectionChanged(change.root, change.selectedElements, change.deselectedElements)); } modelRootChanged(root) { this.updateSelection(root, [], []); } updateSelection(newRoot, select, deselect) { if (newRoot === undefined && select.length === 0 && deselect.length === 0) { return; } const prevRoot = this.root; const prevSelectedElementIDs = new Set(this.selectedElementIDs); this.root = newRoot; // We only select elements that are not part of the deselection const toSelect = [...select].filter(selectId => deselect.indexOf(selectId) === -1); // We only need to deselect elements that are not part of the selection // If an element is part of both the select and deselect, it's state is not changed const toDeselect = [...deselect].filter(deselectId => select.indexOf(deselectId) === -1 && this.selectedElementIDs.has(deselectId)); // update selected element ids toDeselect.forEach(toDeselectId => this.selectedElementIDs.delete(toDeselectId)); toSelect.forEach(toSelectId => this.selectedElementIDs.add(toSelectId)); // check if the newly or previously selected elements still exist in the updated root const deselectedElementIDs = new Set(toDeselect); for (const id of this.selectedElementIDs) { const element = newRoot.index.getById(id); if (element === undefined) { // element to be selected does not exist in the root... this.selectedElementIDs.delete(id); if (prevRoot === null || prevRoot === void 0 ? void 0 : prevRoot.index.getById(id)) { // ...but existed in the previous root, so we want to consider it deselected deselectedElementIDs.add(id); } } } // only send out changes if there actually are changes, i.e., any of the selected elements ids has changed const selectionChanged = prevSelectedElementIDs.size !== this.selectedElementIDs.size || ![...prevSelectedElementIDs].every(value => this.selectedElementIDs.has(value)); if (selectionChanged) { // aggregate to feedback action handling all elements as only the last feedback is restored this.dispatchFeedback([ SelectFeedbackAction.create({ selectedElementsIDs: [...this.selectedElementIDs], deselectedElementsIDs: [...deselectedElementIDs] }) ]); // notify listeners after the feedback action this.notifyListeners(this.root, this.selectedElementIDs, deselectedElementIDs); } } dispatchFeedback(actions) { this.feedbackDispatcher.registerFeedback(this, actions); } notifyListeners(root, selectedElementIDs, deselectedElementIDs) { this.onSelectionChangedEmitter.fire({ root, selectedElements: Array.from(selectedElementIDs), deselectedElements: Array.from(deselectedElementIDs) }); } getModelRoot() { return this.root; } getSelectedElements() { return !this.root ? [] : (0, gmodel_util_1.getElements)(this.root.index, Array.from(this.selectedElementIDs), sprotty_1.isSelectable); } /** * QUERY METHODS */ getSelectedElementIDs() { return [...this.selectedElementIDs]; } hasSelectedElements() { return this.selectedElementIDs.size > 0; } isSingleSelection() { return this.selectedElementIDs.size === 1; } isMultiSelection() { return this.selectedElementIDs.size > 1; } }; exports.SelectionService = SelectionService; __decorate([ (0, inversify_1.inject)(sprotty_1.TYPES.IFeedbackActionDispatcher), __metadata("design:type", Object) ], SelectionService.prototype, "feedbackDispatcher", void 0); __decorate([ (0, inversify_1.inject)(sprotty_1.LazyInjector), __metadata("design:type", Object) ], SelectionService.prototype, "lazyInjector", void 0); __decorate([ (0, inversify_1.inject)(sprotty_1.TYPES.ILogger), __metadata("design:type", Object) ], SelectionService.prototype, "logger", void 0); __decorate([ (0, inversify_1.postConstruct)(), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", void 0) ], SelectionService.prototype, "initialize", null); __decorate([ (0, inversify_1.preDestroy)(), __metadata("design:type", Function), __metadata("design:paramtypes", []), __metadata("design:returntype", void 0) ], SelectionService.prototype, "dispose", null); exports.SelectionService = SelectionService = __decorate([ (0, inversify_1.injectable)() ], SelectionService); /** * Handles a {@link SelectAction} and propagates the new selection to the {@link SelectionService}. * Other tools might be selection-sensitive which means {@link SelectAction}s must be processed as fast as possible. * Handling the action with a command ensures that the action is processed before the next render tick. */ let SelectCommand = class SelectCommand extends sprotty_1.Command { constructor(action, selectionService) { super(); this.action = action; this.selectionService = selectionService; this.selected = []; this.deselected = []; } execute(context) { const model = context.root; const selectionGuard = (element) => element instanceof sprotty_1.GChildElement && (0, sprotty_1.isSelectable)(element); const selectedElements = (0, gmodel_util_1.getElements)(model.index, this.action.selectedElementsIDs, selectionGuard); const deselectedElements = this.action.deselectAll ? this.selectionService.getSelectedElements() : (0, gmodel_util_1.getElements)(model.index, this.action.deselectedElementsIDs, selectionGuard); this.selectionService.updateSelection(model, (0, sprotty_1.pluck)(selectedElements, 'id'), (0, sprotty_1.pluck)(deselectedElements, 'id')); return model; } // Basically no-op since client-side undo is not supported in GLSP. undo(context) { return context.root; } // Basically no-op since client-side redo is not supported in GLSP. redo(context) { return context.root; } }; exports.SelectCommand = SelectCommand; SelectCommand.KIND = sprotty_1.SprottySelectCommand.KIND; exports.SelectCommand = SelectCommand = __decorate([ (0, inversify_1.injectable)(), __param(0, (0, inversify_1.inject)(sprotty_1.TYPES.Action)), __param(1, (0, inversify_1.inject)(SelectionService)), __metadata("design:paramtypes", [Object, SelectionService]) ], SelectCommand); /** * Handles a {@link SelectAllAction} and propagates the new selection to the {@link SelectionService}. * Other tools might be selection-sensitive which means {@link SelectionAllAction}s must be processed as fast as possible. * Handling the action with a command ensures that the action is processed before the next render tick. */ let SelectAllCommand = class SelectAllCommand extends sprotty_1.Command { constructor(action, selectionService) { super(); this.action = action; this.selectionService = selectionService; this.previousSelection = new Map(); } execute(context) { const model = context.root; const selectionGuard = (element) => element instanceof sprotty_1.GChildElement && (0, sprotty_1.isSelectable)(element); const selectables = (0, gmodel_util_1.getMatchingElements)(model.index, selectionGuard); const selectableIds = (0, sprotty_1.pluck)(selectables, 'id'); if (this.action.select) { this.selectionService.updateSelection(model, selectableIds, []); } else { this.selectionService.updateSelection(model, [], selectableIds); } return model; } // Basically no-op since client-side undo is not supported in GLSP. undo(context) { return context.root; } // Basically no-op since client-side redo is not supported in GLSP. redo(context) { return context.root; } }; exports.SelectAllCommand = SelectAllCommand; SelectAllCommand.KIND = sprotty_1.SprottySelectAllCommand.KIND; exports.SelectAllCommand = SelectAllCommand = __decorate([ (0, inversify_1.injectable)(), __param(0, (0, inversify_1.inject)(sprotty_1.TYPES.Action)), __param(1, (0, inversify_1.inject)(SelectionService)), __metadata("design:paramtypes", [Object, SelectionService]) ], SelectAllCommand); var SelectFeedbackAction; (function (SelectFeedbackAction) { SelectFeedbackAction.KIND = 'selectFeedback'; function is(object) { return sprotty_1.Action.hasKind(object, SelectFeedbackAction.KIND) && (0, sprotty_1.hasArrayProp)(object, 'selectedElementsIDs') && (0, sprotty_1.hasArrayProp)(object, 'deselectedElementsIDs'); } SelectFeedbackAction.is = is; function create(options) { return { ...sprotty_1.SelectAction.create(options), kind: SelectFeedbackAction.KIND }; } SelectFeedbackAction.create = create; function addSelection(selectedElementsIDs) { return { ...sprotty_1.SelectAction.addSelection(selectedElementsIDs), kind: SelectFeedbackAction.KIND }; } SelectFeedbackAction.addSelection = addSelection; function removeSelection(deselectedElementsIDs) { return { ...sprotty_1.SelectAction.removeSelection(deselectedElementsIDs), kind: SelectFeedbackAction.KIND }; } SelectFeedbackAction.removeSelection = removeSelection; function setSelection(selectedElementsIDs) { return { ...sprotty_1.SelectAction.setSelection(selectedElementsIDs), kind: SelectFeedbackAction.KIND }; } SelectFeedbackAction.setSelection = setSelection; })(SelectFeedbackAction || (exports.SelectFeedbackAction = SelectFeedbackAction = {})); //# sourceMappingURL=selection-service.js.map