@eclipse-glsp/client
Version:
A sprotty-based client for GLSP
193 lines • 8.86 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FeedbackMoveMouseListener = 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 lodash_1 = require("lodash");
const drag_aware_mouse_listener_1 = require("../../../base/drag-aware-mouse-listener");
const css_feedback_1 = require("../../../base/feedback/css-feedback");
const gmodel_util_1 = require("../../../utils/gmodel-util");
const model_1 = require("../../change-bounds/model");
const change_bounds_tool_feedback_1 = require("./change-bounds-tool-feedback");
/**
* This mouse listener provides visual feedback for moving by sending client-side
* `MoveAction`s while elements are selected and dragged. This will also update
* their bounds, which is important, as it is not only required for rendering
* the visual feedback but also the basis for sending the change to the server
* (see also `tools/MoveTool`).
*/
class FeedbackMoveMouseListener extends drag_aware_mouse_listener_1.DragAwareMouseListener {
constructor(tool) {
super();
this.tool = tool;
this.elementId2startPos = new Map();
this.tracker = tool.createChangeBoundsTracker();
this.moveInitializedFeedback = tool.createFeedbackEmitter();
this.moveFeedback = tool.createFeedbackEmitter();
}
mouseDown(target, event) {
super.mouseDown(target, event);
if (event.button === 0) {
if (this.tracker.isTracking()) {
// we have a move in progress that was not resolved yet (e.g., user may have triggered a mouse up outside the window)
this.draggingMouseUp(target, event);
return [];
}
this.initializeMove(target, event);
return [];
}
this.tracker.stopTracking();
return [];
}
initializeMove(target, event) {
if (target instanceof model_1.GResizeHandle) {
// avoid conflict with resize tool
return;
}
const moveable = (0, sprotty_1.findParentByFeature)(target, this.isValidMoveable);
if (moveable !== undefined) {
this.tracker.startTracking();
this.scheduleMoveInitialized();
}
else {
this.tracker.stopTracking();
}
}
scheduleMoveInitialized() {
var _a;
(_a = this.pendingMoveInitialized) === null || _a === void 0 ? void 0 : _a.cancel();
this.pendingMoveInitialized = (0, lodash_1.debounce)(() => {
this.moveInitialized();
this.pendingMoveInitialized = undefined;
}, 750);
this.pendingMoveInitialized();
}
moveInitializationTimeout() {
return 750;
}
moveInitialized() {
if (this.isMouseDown) {
this.moveInitializedFeedback
.add(change_bounds_tool_feedback_1.MoveInitializedEventAction.create(), change_bounds_tool_feedback_1.MoveFinishedEventAction.create())
.add((0, css_feedback_1.cursorFeedbackAction)(css_feedback_1.CursorCSS.MOVE), (0, css_feedback_1.cursorFeedbackAction)(css_feedback_1.CursorCSS.DEFAULT))
.submit();
}
}
isValidMoveable(element) {
return !!element && (0, gmodel_util_1.isNonRoutableSelectedMovableBoundsAware)(element) && !(element instanceof model_1.GResizeHandle);
}
isValidRevertable(element) {
return !!element && (0, gmodel_util_1.isNonRoutableMovableBoundsAware)(element) && !(element instanceof model_1.GResizeHandle);
}
nonDraggingMouseUp(element, event) {
// should reset everything that may have happend on mouse down
this.moveInitializedFeedback.dispose();
this.tracker.stopTracking();
return [];
}
draggingMouseMove(target, event) {
super.draggingMouseMove(target, event);
if (this.tracker.isTracking()) {
return this.moveElements(target, event);
}
return [];
}
moveElements(target, event) {
var _a;
if (this.elementId2startPos.size === 0) {
this.initializeElementsToMove(target.root);
}
const elementsToMove = this.getElementsToMove(target);
const move = this.tracker.moveElements(elementsToMove, { snap: event, restrict: event });
if (move.elementMoves.length === 0) {
return [];
}
// cancel any pending move
(_a = this.pendingMoveInitialized) === null || _a === void 0 ? void 0 : _a.cancel();
this.moveFeedback.add(this.createMoveAction(move), () => this.resetElementPositions(target));
this.addMoveFeedback(move, target, event);
this.tracker.updateTrackingPosition(move);
this.moveFeedback.submit();
return [];
}
createMoveAction(trackedMove) {
// we never want to animate the move action as this interferes with the move feedback
return sprotty_1.MoveAction.create(trackedMove.elementMoves.map(move => ({ elementId: move.element.id, toPosition: move.toPosition })), { animate: false });
}
addMoveFeedback(trackedMove, ctx, event) {
this.tool.changeBoundsManager.addMoveFeedback(this.moveFeedback, trackedMove, ctx, event);
}
initializeElementsToMove(root) {
const elementsToMove = this.collectElementsToMove(root);
elementsToMove.forEach(element => this.elementId2startPos.set(element.id, element.position));
}
collectElementsToMove(root) {
const moveableElements = (0, gmodel_util_1.filter)(root.index, this.isValidMoveable);
const topLevelElements = (0, gmodel_util_1.removeDescendants)(moveableElements);
return Array.from(topLevelElements);
}
getElementsToMove(context, moveable = this.isValidMoveable) {
return (0, gmodel_util_1.getElements)(context.root.index, Array.from(this.elementId2startPos.keys()), moveable);
}
resetElementPositions(context) {
const elementMoves = this.revertElementMoves(context);
return sprotty_1.MoveAction.create(elementMoves, { animate: false, finished: true });
}
revertElementMoves(context) {
var _a;
const elementMoves = [];
if ((_a = context === null || context === void 0 ? void 0 : context.root) === null || _a === void 0 ? void 0 : _a.index) {
const movableElements = this.getElementsToMove(context, this.isValidRevertable);
movableElements.forEach(element => elementMoves.push({ elementId: element.id, toPosition: this.elementId2startPos.get(element.id) }));
}
return elementMoves;
}
draggingMouseUp(target, event) {
if (!this.tracker.isTracking()) {
return [];
}
const elementsToMove = this.getElementsToMove(target);
if (!this.tool.movementOptions.allElementsNeedToBeValid) {
// only reset the move of invalid elements, the others will be handled by the change bounds tool itself
elementsToMove
.filter(element => this.tool.changeBoundsManager.isValid(element))
.forEach(element => this.elementId2startPos.delete(element.id));
}
else {
if (elementsToMove.length > 0 && elementsToMove.every(element => this.tool.changeBoundsManager.isValid(element))) {
// do not reset any element as all are valid
this.elementId2startPos.clear();
}
}
this.dispose();
return [];
}
selectionChanged(root, selectedElements, deselectedElements) {
this.dispose();
}
dispose() {
var _a;
(_a = this.pendingMoveInitialized) === null || _a === void 0 ? void 0 : _a.cancel();
this.moveInitializedFeedback.dispose();
this.moveFeedback.dispose();
this.tracker.dispose();
this.elementId2startPos.clear();
super.dispose();
}
}
exports.FeedbackMoveMouseListener = FeedbackMoveMouseListener;
//# sourceMappingURL=change-bounds-tool-move-feedback.js.map