@eclipse-glsp/client
Advanced tools
Comparing version 0.8.0-next.55671b3 to 0.8.0-next.7052c9c
import { Action, ActionHandlerRegistry, CommandExecutionContext, CommandReturn, IActionHandler, ILogger, SetModelAction, SModelRoot } from "sprotty/lib"; | ||
import { UpdateModelAction, UpdateModelCommand } from "sprotty/lib/features/update/update-model"; | ||
import { IFeedbackActionDispatcher } from "src/features/tool-feedback/feedback-action-dispatcher"; | ||
import { IFeedbackActionDispatcher } from "../../features/tool-feedback/feedback-action-dispatcher"; | ||
export declare class SetModelActionHandler implements IActionHandler { | ||
@@ -18,4 +18,4 @@ handle(action: Action): Action | void; | ||
protected readonly feedbackActionDispatcher: IFeedbackActionDispatcher; | ||
protected actionHandlerRegistryProvider: () => Promise<ActionHandlerRegistry>; | ||
protected modelRootListeners: SModelRootListener[]; | ||
protected actionHandlerRegistry?: ActionHandlerRegistry; | ||
constructor(action: UpdateModelAction, logger: ILogger, feedbackActionDispatcher: IFeedbackActionDispatcher, actionHandlerRegistryProvider: () => Promise<ActionHandlerRegistry>, modelRootListeners?: SModelRootListener[]); | ||
@@ -22,0 +22,0 @@ protected performUpdate(oldRoot: SModelRoot, newRoot: SModelRoot, context: CommandExecutionContext): CommandReturn; |
@@ -80,9 +80,8 @@ "use strict"; | ||
_this.feedbackActionDispatcher = feedbackActionDispatcher; | ||
_this.actionHandlerRegistryProvider = actionHandlerRegistryProvider; | ||
_this.modelRootListeners = modelRootListeners; | ||
actionHandlerRegistryProvider().then(function (registry) { return _this.actionHandlerRegistry = registry; }); | ||
return _this; | ||
} | ||
FeedbackAwareUpdateModelCommand.prototype.performUpdate = function (oldRoot, newRoot, context) { | ||
var _this = this; | ||
if (this.feedbackActionDispatcher) { | ||
if (this.feedbackActionDispatcher && this.actionHandlerRegistry) { | ||
// Create a temporary context wich defines the `newRoot` as `root` | ||
@@ -98,6 +97,4 @@ // This way we do not corrupt the redo/undo behavior of the super class | ||
}; | ||
this.actionHandlerRegistryProvider().then(function (registry) { | ||
var feedbackCommands = _this.getFeedbackCommands(registry); | ||
feedbackCommands.forEach(function (command) { return command.execute(tempContext_1); }); | ||
}); | ||
var feedbackCommands = this.getFeedbackCommands(this.actionHandlerRegistry); | ||
feedbackCommands.forEach(function (command) { return command.execute(tempContext_1); }); | ||
} | ||
@@ -104,0 +101,0 @@ this.modelRootListeners.forEach(function (listener) { return listener.modelRootChanged(newRoot); }); |
@@ -1,30 +0,13 @@ | ||
import { Action, Bounds, BoundsAware, Point, PointToPointLine, SModelElement } from "sprotty/lib"; | ||
import { Action, BoundsAware, Point, SModelElement } from "sprotty/lib"; | ||
export interface IMovementRestrictor { | ||
attemptMove(element: SModelElement, mousePoint: Point, target: SModelElement, delta: Point, result: Action[]): boolean; | ||
validate(newLocation: Point, element: SModelElement): boolean; | ||
cssClasses?: string[]; | ||
} | ||
export declare class NoCollisionMovementRestrictor { | ||
hasCollided: boolean; | ||
attemptMove(element: SModelElement, mousePoint: Point, target: SModelElement, delta: Point, result: Action[]): boolean; | ||
/** | ||
* Used to return the collision target(s) or the collision chain in case of multiple selected elements | ||
*/ | ||
getCollisionChain(target: SModelElement, element: SModelElement, delta: Point, collisionChain: SModelElement[]): SModelElement[]; | ||
/** | ||
* Returns bounds centered around the point | ||
*/ | ||
getCenteredBoundsToPointer(mousePoint: Point, bounds: Bounds): Bounds; | ||
getDistanceBetweenParallelLines(p1: Point, p2: Point, secondLine: PointToPointLine): Number; | ||
/** | ||
* Snaps the element to the target in case of a collision | ||
*/ | ||
getSnappedBounds(element: SModelElement & BoundsAware, target: SModelElement & BoundsAware): Bounds; | ||
export declare function createMovementRestrictionFeedback(element: SModelElement, movementRestrictor: IMovementRestrictor): Action[]; | ||
export declare function removeMovementRestrictionFeedback(element: SModelElement, movementRestrictor: IMovementRestrictor): Action[]; | ||
export declare class NoOverlapMovmentRestrictor implements IMovementRestrictor { | ||
validate(newLocation: Point, element: SModelElement): boolean; | ||
cssClasses: string[]; | ||
} | ||
/** | ||
* Used to check if 1D boxes (lines) overlap | ||
*/ | ||
export declare function isOverlapping1Dimension(x1: number, width1: number, x2: number, width2: number): boolean; | ||
/** | ||
* Used to check if 2 bounds are overlapping | ||
*/ | ||
export declare function isOverlappingBounds(bounds1: Bounds, bounds2: Bounds): boolean; | ||
export declare function areOverlapping(element1: SModelElement & BoundsAware, element2: SModelElement & BoundsAware): boolean; | ||
//# sourceMappingURL=movement-restrictor.d.ts.map |
@@ -26,242 +26,63 @@ "use strict"; | ||
var lib_1 = require("sprotty/lib"); | ||
var iterable_1 = require("sprotty/lib/utils/iterable"); | ||
var cursor_feedback_1 = require("../tool-feedback/cursor-feedback"); | ||
var viewpoint_util_1 = require("../../utils/viewpoint-util"); | ||
var css_feedback_1 = require("../tool-feedback/css-feedback"); | ||
var model_1 = require("./model"); | ||
var NoCollisionMovementRestrictor = /** @class */ (function () { | ||
function NoCollisionMovementRestrictor() { | ||
this.hasCollided = false; | ||
function createMovementRestrictionFeedback(element, movementRestrictor) { | ||
var result = []; | ||
result.push(new css_feedback_1.ModifyCSSFeedbackAction(element, movementRestrictor.cssClasses)); | ||
if (element instanceof lib_1.SParentElement) { | ||
element.children.filter(function (child) { return child instanceof model_1.SResizeHandle; }).forEach(function (child) { return result.push(new css_feedback_1.ModifyCSSFeedbackAction(child, movementRestrictor.cssClasses)); }); | ||
} | ||
/* | ||
* Attempt to perform an element move. Returns true if the move is not restricted anc can be applied successfull and false otherwise | ||
*/ | ||
NoCollisionMovementRestrictor.prototype.attemptMove = function (element, mousePoint, target, delta, result) { | ||
var _this = this; | ||
return result; | ||
} | ||
exports.createMovementRestrictionFeedback = createMovementRestrictionFeedback; | ||
function removeMovementRestrictionFeedback(element, movementRestrictor) { | ||
var result = []; | ||
result.push(new css_feedback_1.ModifyCSSFeedbackAction(element, undefined, movementRestrictor.cssClasses)); | ||
if (element instanceof lib_1.SParentElement) { | ||
element.children.filter(function (child) { return child instanceof model_1.SResizeHandle; }). | ||
forEach(function (child) { return result.push(new css_feedback_1.ModifyCSSFeedbackAction(child, undefined, movementRestrictor.cssClasses)); }); | ||
} | ||
return result; | ||
} | ||
exports.removeMovementRestrictionFeedback = removeMovementRestrictionFeedback; | ||
var NoOverlapMovmentRestrictor = /** @class */ (function () { | ||
function NoOverlapMovmentRestrictor() { | ||
this.cssClasses = ["movement-not-allowed"]; | ||
} | ||
NoOverlapMovmentRestrictor.prototype.validate = function (newLocation, element) { | ||
if (!model_1.isBoundsAwareMoveable(element)) { | ||
return false; | ||
} | ||
var mouseOverElement = false; | ||
var willOverlap = false; | ||
// Create ghost element to check possible bounds | ||
// Create ghost element at the newLocation; | ||
var ghostElement = Object.create(element); | ||
ghostElement.bounds = this.getCenteredBoundsToPointer(mousePoint, element.bounds); | ||
// Set type to Ghost to keep tracking it through elements | ||
ghostElement.bounds.x = newLocation.x - element.bounds.width / 2; | ||
ghostElement.bounds.y = newLocation.y - element.bounds.height / 2; | ||
ghostElement.type = "Ghost"; | ||
ghostElement.id = element.id; | ||
// Check collision for gost element (to see when it has passed beyond obstacle) | ||
var collisionTargetsGhost = this.getCollisionChain(target, ghostElement, delta, []) | ||
.filter(function (collidingElement) { return lib_1.isSelectable(collidingElement) && !collidingElement.selected; }); | ||
// After collision the mouse is back inside the element => change cursor back to default | ||
if (this.hasCollided && lib_1.includes(element.bounds, mousePoint)) { | ||
mouseOverElement = true; | ||
result.push(new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.DEFAULT)); | ||
} | ||
var selectedElements = target.root.index.all() | ||
.filter(function (selected) { return lib_1.isSelectable(selected) && selected.selected; }); | ||
// If the ghost element has moved beyond the obstacle move the actual element there aswell | ||
// But only if a single element is selected (multi-selection jumps are not supported) | ||
if (this.hasCollided && collisionTargetsGhost.length === 0 && iterable_1.toArray(selectedElements).length === 1) { | ||
mouseOverElement = true; | ||
result.push(new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.DEFAULT)); | ||
if (element.id === ghostElement.id) { | ||
element.bounds = ghostElement.bounds; | ||
} | ||
} | ||
// Get only the valid, non-slected collision targets to avoid in-selection collisions | ||
var collisionTargets = this.getCollisionChain(target, element, delta, []) | ||
.filter(function (collidingElement) { return lib_1.isSelectable(collidingElement) && !collidingElement.selected; }); | ||
if (collisionTargets.length > 0) { | ||
collisionTargets.forEach(function (collisionTarget) { | ||
if (lib_1.isBoundsAware(collisionTarget)) { | ||
// Only snap on first collision to avoid erratic jumps | ||
if (!_this.hasCollided) { | ||
var snappedBounds = _this.getSnappedBounds(element, collisionTarget); | ||
var snapMoves = []; | ||
snapMoves.push({ | ||
elementId: element.id, | ||
fromPosition: { | ||
x: element.position.x, | ||
y: element.position.y | ||
}, | ||
toPosition: { | ||
x: snappedBounds.x, | ||
y: snappedBounds.y | ||
} | ||
}); | ||
result.push(new lib_1.MoveAction(snapMoves, false)); | ||
} | ||
willOverlap = true; | ||
_this.hasCollided = true; | ||
result.push(new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.OVERLAP_FORBIDDEN)); | ||
} | ||
}); | ||
} | ||
if ((!willOverlap && !this.hasCollided) || | ||
(this.hasCollided && !willOverlap && mouseOverElement)) { | ||
this.hasCollided = false; | ||
return true; | ||
} | ||
return false; | ||
return !Array.from(element.root.index.all().filter(function (e) { return e.id !== ghostElement.id && e !== ghostElement.root && (e instanceof lib_1.SNode); }) | ||
.map(function (e) { return e; })).some(function (e) { return areOverlapping(e, ghostElement); }); | ||
}; | ||
/** | ||
* Used to return the collision target(s) or the collision chain in case of multiple selected elements | ||
*/ | ||
NoCollisionMovementRestrictor.prototype.getCollisionChain = function (target, element, delta, collisionChain) { | ||
var _this = this; | ||
if (model_1.isBoundsAwareMoveable(element)) { | ||
target.root.index.all() | ||
.filter(function (candidate) { return lib_1.isSelectable(candidate) && element.id !== candidate.id && collisionChain.indexOf(candidate) < 0; }) | ||
.forEach(function (candidate) { | ||
if (lib_1.isMoveable(element) && lib_1.isMoveable(candidate)) { | ||
if (lib_1.isBoundsAware(element) && lib_1.isBoundsAware(candidate)) { | ||
var futureBounds = { | ||
x: element.position.x + delta.x, | ||
y: element.position.y + delta.y, | ||
width: element.bounds.width, | ||
height: element.bounds.height | ||
}; | ||
if (isOverlappingBounds(futureBounds, candidate.bounds) && (!isOverlappingBounds(element.bounds, candidate.bounds) || element.type === "Ghost")) { | ||
collisionChain.push(candidate); | ||
if (lib_1.isSelectable(candidate) && candidate.selected) { | ||
// Check what the selected candidate will collide with and add it to the chain | ||
collisionChain.push.apply(collisionChain, _this.getCollisionChain(target, candidate, delta, collisionChain)); | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
return collisionChain; | ||
}; | ||
/** | ||
* Returns bounds centered around the point | ||
*/ | ||
NoCollisionMovementRestrictor.prototype.getCenteredBoundsToPointer = function (mousePoint, bounds) { | ||
var middleX = mousePoint.x - bounds.width / 2; | ||
var middleY = mousePoint.y - bounds.height / 2; | ||
var shiftedBounds = { x: middleX, y: middleY, width: bounds.width, height: bounds.height }; | ||
return shiftedBounds; | ||
}; | ||
// Remove this and use the one from the improved routing branch | ||
NoCollisionMovementRestrictor.prototype.getDistanceBetweenParallelLines = function (p1, p2, secondLine) { | ||
var numerator = Math.abs((secondLine.a * p1.x) + (secondLine.b * p1.y) - secondLine.c); | ||
var denominator = Math.sqrt(Math.pow(secondLine.a, 2) + Math.pow(secondLine.b, 2)); | ||
return numerator / denominator; | ||
}; | ||
/** | ||
* Snaps the element to the target in case of a collision | ||
*/ | ||
NoCollisionMovementRestrictor.prototype.getSnappedBounds = function (element, target) { | ||
var snappedBounds = element.bounds; | ||
// Build corner points | ||
var elementTopLeft = { | ||
x: element.bounds.x, | ||
y: element.bounds.y | ||
}; | ||
var elementTopRight = { | ||
x: element.bounds.x + element.bounds.width, | ||
y: element.bounds.y | ||
}; | ||
var elementBottomLeft = { | ||
x: element.bounds.x, | ||
y: element.bounds.y + element.bounds.height | ||
}; | ||
var elementBottomRight = { | ||
x: element.bounds.x + element.bounds.width, | ||
y: element.bounds.y + element.bounds.height | ||
}; | ||
var targetTopLeft = { | ||
x: target.bounds.x, | ||
y: target.bounds.y | ||
}; | ||
var targetTopRight = { | ||
x: target.bounds.x + target.bounds.width, | ||
y: target.bounds.y | ||
}; | ||
var targetBottomLeft = { | ||
x: target.bounds.x, | ||
y: target.bounds.y + target.bounds.height | ||
}; | ||
var targetBottomRight = { | ||
x: target.bounds.x + target.bounds.width, | ||
y: target.bounds.y + target.bounds.height | ||
}; | ||
// Build lines | ||
var targetTopLine = new lib_1.PointToPointLine(targetTopLeft, targetTopRight); | ||
var targetBottomLine = new lib_1.PointToPointLine(targetBottomLeft, targetBottomRight); | ||
var targetLeftLine = new lib_1.PointToPointLine(targetTopLeft, targetBottomLeft); | ||
var targetRightLine = new lib_1.PointToPointLine(targetTopRight, targetBottomRight); | ||
// Compute distances | ||
var distanceTop = this.getDistanceBetweenParallelLines(elementBottomLeft, elementBottomRight, targetTopLine); | ||
var distanceBottom = this.getDistanceBetweenParallelLines(elementTopLeft, elementTopRight, targetBottomLine); | ||
var distanceLeft = this.getDistanceBetweenParallelLines(elementTopLeft, elementBottomLeft, targetRightLine); | ||
var distanceRight = this.getDistanceBetweenParallelLines(elementTopRight, elementBottomRight, targetLeftLine); | ||
var minimumCandidates = []; | ||
// Overlap on the horizontal lines | ||
if (isOverlapping1Dimension(element.bounds.x, element.bounds.width, target.bounds.x, target.bounds.width)) { | ||
minimumCandidates.push(distanceTop.valueOf()); | ||
minimumCandidates.push(distanceBottom.valueOf()); | ||
} | ||
// Overlap on the horizontal lines | ||
if (isOverlapping1Dimension(element.bounds.y, element.bounds.height, target.bounds.y, target.bounds.height)) { | ||
minimumCandidates.push(distanceLeft.valueOf()); | ||
minimumCandidates.push(distanceRight.valueOf()); | ||
} | ||
// Get minimum distance and then snap accordingly | ||
minimumCandidates.sort(function (a, b) { return a - b; }); | ||
var minimumDistance = minimumCandidates[0]; | ||
if (minimumDistance === distanceTop) { | ||
snappedBounds = { | ||
x: element.bounds.x, | ||
y: target.bounds.y - 1 - element.bounds.height, | ||
width: element.bounds.width, | ||
height: element.bounds.height | ||
}; | ||
} | ||
if (minimumDistance === distanceBottom) { | ||
snappedBounds = { | ||
x: element.bounds.x, | ||
y: target.bounds.y + target.bounds.height + 1, | ||
width: element.bounds.width, | ||
height: element.bounds.height | ||
}; | ||
} | ||
if (minimumDistance === distanceLeft) { | ||
snappedBounds = { | ||
x: target.bounds.x + target.bounds.width + 1, | ||
y: element.bounds.y, | ||
width: element.bounds.width, | ||
height: element.bounds.height | ||
}; | ||
} | ||
if (minimumDistance === distanceRight) { | ||
snappedBounds = { | ||
x: target.bounds.x - 1 - element.bounds.width, | ||
y: element.bounds.y, | ||
width: element.bounds.width, | ||
height: element.bounds.height | ||
}; | ||
} | ||
return snappedBounds; | ||
}; | ||
NoCollisionMovementRestrictor = __decorate([ | ||
NoOverlapMovmentRestrictor = __decorate([ | ||
inversify_1.injectable() | ||
], NoCollisionMovementRestrictor); | ||
return NoCollisionMovementRestrictor; | ||
], NoOverlapMovmentRestrictor); | ||
return NoOverlapMovmentRestrictor; | ||
}()); | ||
exports.NoCollisionMovementRestrictor = NoCollisionMovementRestrictor; | ||
/** | ||
* Used to check if 1D boxes (lines) overlap | ||
*/ | ||
function isOverlapping1Dimension(x1, width1, x2, width2) { | ||
return x1 + width1 >= x2 && x2 + width2 >= x1; | ||
exports.NoOverlapMovmentRestrictor = NoOverlapMovmentRestrictor; | ||
function areOverlapping(element1, element2) { | ||
var b1 = viewpoint_util_1.toAbsoluteBounds(element1); | ||
var b2 = viewpoint_util_1.toAbsoluteBounds(element2); | ||
var r1TopLeft = b1; | ||
var r1BottomRight = { x: b1.x + b1.width, y: b1.y + b1.height }; | ||
var r2TopLeft = b2; | ||
var r2BottomRight = { x: b2.x + b2.width, y: b2.y + b2.height }; | ||
// If one rectangle is on left side of other | ||
if (r1TopLeft.x > r2BottomRight.x || r2TopLeft.x > r1BottomRight.x) | ||
return false; | ||
// If one rectangle is above other | ||
if (r1BottomRight.y < r2TopLeft.y || r2BottomRight.y < r1TopLeft.y) | ||
return false; | ||
return true; | ||
} | ||
exports.isOverlapping1Dimension = isOverlapping1Dimension; | ||
/** | ||
* Used to check if 2 bounds are overlapping | ||
*/ | ||
function isOverlappingBounds(bounds1, bounds2) { | ||
return isOverlapping1Dimension(bounds1.x, bounds1.width, bounds2.x, bounds2.width) && | ||
isOverlapping1Dimension(bounds1.y, bounds1.height, bounds2.y, bounds2.height); | ||
} | ||
exports.isOverlappingBounds = isOverlappingBounds; | ||
exports.areOverlapping = areOverlapping; | ||
//# sourceMappingURL=movement-restrictor.js.map |
@@ -32,3 +32,2 @@ "use strict"; | ||
var lib_1 = require("sprotty/lib"); | ||
var smodel_util_1 = require("../../utils/smodel-util"); | ||
var action_definitions_1 = require("../context-actions/action-definitions"); | ||
@@ -49,3 +48,3 @@ var glsp_action_dispatcher_1 = require("../request-response/glsp-action-dispatcher"); | ||
var _this = this; | ||
var selectedElementIds = Array.from(root.index.all().filter(smodel_util_1.isSelected).map(function (e) { return e.id; })); | ||
var selectedElementIds = Array.from(root.index.all().filter(lib_1.isSelected).map(function (e) { return e.id; })); | ||
var requestAction = new action_definitions_1.RequestContextActions(selectedElementIds, lastMousePosition, (_a = {}, | ||
@@ -52,0 +51,0 @@ _a[action_definitions_1.ContextActions.UI_CONTROL_KEY] = ServerCommandPalette.KEY, |
@@ -20,24 +20,7 @@ "use strict"; | ||
var lib_1 = require("sprotty/lib"); | ||
var types_1 = require("../../types"); | ||
var menu_providers_1 = require("./menu-providers"); | ||
var mouse_listener_1 = require("./mouse-listener"); | ||
var server_context_menu_provider_1 = require("./server-context-menu-provider"); | ||
var glspContextMenuModule = new inversify_1.ContainerModule(function (bind) { | ||
bind(types_1.GLSP_TYPES.IContextMenuServiceProvider).toProvider(function (ctx) { | ||
return function () { | ||
return new Promise(function (resolve, reject) { | ||
if (ctx.container.isBound(types_1.GLSP_TYPES.IContextMenuService)) { | ||
resolve(ctx.container.get(types_1.GLSP_TYPES.IContextMenuService)); | ||
} | ||
else { | ||
reject(); | ||
} | ||
}); | ||
}; | ||
}); | ||
bind(lib_1.TYPES.MouseListener).to(mouse_listener_1.ContextMenuMouseListener); | ||
bind(types_1.GLSP_TYPES.IContextMenuProviderRegistry).to(menu_providers_1.ContextMenuProviderRegistry); | ||
bind(types_1.GLSP_TYPES.IContextMenuProvider).to(server_context_menu_provider_1.ServerContextMenuItemProvider); | ||
bind(lib_1.TYPES.IContextMenuItemProvider).to(server_context_menu_provider_1.ServerContextMenuItemProvider); | ||
}); | ||
exports.default = glspContextMenuModule; | ||
//# sourceMappingURL=di.config.js.map |
@@ -1,4 +0,3 @@ | ||
import { Action, LabeledAction, Point, SModelElement } from "sprotty/lib"; | ||
import { Action, IContextMenuItemProvider, LabeledAction, Point, SModelElement } from "sprotty/lib"; | ||
import { GLSPActionDispatcher } from "../request-response/glsp-action-dispatcher"; | ||
import { IContextMenuItemProvider } from "./menu-providers"; | ||
export declare namespace ServerContextMenu { | ||
@@ -5,0 +4,0 @@ const KEY = "context-menu"; |
@@ -32,3 +32,2 @@ "use strict"; | ||
var lib_1 = require("sprotty/lib"); | ||
var smodel_util_1 = require("../../utils/smodel-util"); | ||
var action_definitions_1 = require("../context-actions/action-definitions"); | ||
@@ -47,3 +46,3 @@ var glsp_action_dispatcher_1 = require("../request-response/glsp-action-dispatcher"); | ||
var _this = this; | ||
var selectedElementIds = Array.from(root.index.all().filter(smodel_util_1.isSelected).map(function (e) { return e.id; })); | ||
var selectedElementIds = Array.from(root.index.all().filter(lib_1.isSelected).map(function (e) { return e.id; })); | ||
var localPosition = lastMousePosition ? root.root.parentToLocal(lib_1.subtract(lastMousePosition, root.root.canvasBounds)) : undefined; | ||
@@ -50,0 +49,0 @@ var requestAction = new action_definitions_1.RequestContextActions(selectedElementIds, localPosition, (_a = {}, _a[action_definitions_1.ContextActions.UI_CONTROL_KEY] = ServerContextMenu.KEY, _a)); |
@@ -38,3 +38,3 @@ /******************************************************************************** | ||
newBounds: ElementAndBounds[]; | ||
readonly kind = "changeBoundsOperation"; | ||
readonly kind = "changeBounds"; | ||
constructor(newBounds: ElementAndBounds[]); | ||
@@ -49,2 +49,14 @@ } | ||
} | ||
export declare class ReconnectConnectionOperationAction implements Action { | ||
readonly connectionElementId: string; | ||
readonly sourceElementId: string; | ||
readonly targetElementId: string; | ||
readonly kind = "reconnectConnection"; | ||
constructor(connectionElementId: string, sourceElementId: string, targetElementId: string); | ||
} | ||
export declare class ChangeRoutingPointsOperation implements Action { | ||
newRoutingPoints: ElementAndRoutingPoints[]; | ||
readonly kind = "changeRoutingPoints"; | ||
constructor(newRoutingPoints: ElementAndRoutingPoints[]); | ||
} | ||
export declare class GenericOperationAction implements Action { | ||
@@ -57,2 +69,6 @@ readonly id: string; | ||
} | ||
export interface ElementAndRoutingPoints { | ||
elementId: string; | ||
newRoutingPoints?: Point[]; | ||
} | ||
//# sourceMappingURL=operation-actions.d.ts.map |
@@ -50,2 +50,20 @@ "use strict"; | ||
exports.ChangeContainerOperation = ChangeContainerOperation; | ||
var ReconnectConnectionOperationAction = /** @class */ (function () { | ||
function ReconnectConnectionOperationAction(connectionElementId, sourceElementId, targetElementId) { | ||
this.connectionElementId = connectionElementId; | ||
this.sourceElementId = sourceElementId; | ||
this.targetElementId = targetElementId; | ||
this.kind = set_operations_1.OperationKind.RECONNECT_CONNECTION; | ||
} | ||
return ReconnectConnectionOperationAction; | ||
}()); | ||
exports.ReconnectConnectionOperationAction = ReconnectConnectionOperationAction; | ||
var ChangeRoutingPointsOperation = /** @class */ (function () { | ||
function ChangeRoutingPointsOperation(newRoutingPoints) { | ||
this.newRoutingPoints = newRoutingPoints; | ||
this.kind = set_operations_1.OperationKind.CHANGE_ROUTING_POINTS; | ||
} | ||
return ChangeRoutingPointsOperation; | ||
}()); | ||
exports.ChangeRoutingPointsOperation = ChangeRoutingPointsOperation; | ||
var GenericOperationAction = /** @class */ (function () { | ||
@@ -52,0 +70,0 @@ function GenericOperationAction(id, elementId, location) { |
@@ -21,5 +21,5 @@ /******************************************************************************** | ||
const RECONNECT_CONNECTION = "reconnectConnection"; | ||
const REROUTE_CONNECTION = "rerouteConnection"; | ||
const DELETE_ELEMENT = "delete"; | ||
const CHANGE_BOUNDS = "changeBoundsOperation"; | ||
const CHANGE_ROUTING_POINTS = "changeRoutingPoints"; | ||
const DELETE_ELEMENT = "deleteElement"; | ||
const CHANGE_BOUNDS = "changeBounds"; | ||
const CHANGE_CONTAINER = "changeContainer"; | ||
@@ -26,0 +26,0 @@ const GENERIC = "generic"; |
@@ -8,5 +8,5 @@ "use strict"; | ||
OperationKind.RECONNECT_CONNECTION = "reconnectConnection"; | ||
OperationKind.REROUTE_CONNECTION = "rerouteConnection"; | ||
OperationKind.DELETE_ELEMENT = "delete"; | ||
OperationKind.CHANGE_BOUNDS = "changeBoundsOperation"; | ||
OperationKind.CHANGE_ROUTING_POINTS = "changeRoutingPoints"; | ||
OperationKind.DELETE_ELEMENT = "deleteElement"; | ||
OperationKind.CHANGE_BOUNDS = "changeBounds"; | ||
OperationKind.CHANGE_CONTAINER = "changeContainer"; | ||
@@ -13,0 +13,0 @@ OperationKind.GENERIC = "generic"; |
import { VNode } from "snabbdom/vnode"; | ||
import { Action, CommandExecutionContext, CommandReturn, MouseListener, Point, SModelElement } from "sprotty/lib"; | ||
import { IMovementRestrictor } from "../change-bounds/movement-restrictor"; | ||
import { Action, CommandExecutionContext, CommandReturn, MouseListener, MoveAction, Point, SModelElement, SModelRoot } from "sprotty/lib"; | ||
import { ChangeBoundsTool } from "../tools/change-bounds-tool"; | ||
import { FeedbackCommand } from "./model"; | ||
@@ -34,8 +34,13 @@ export declare class ShowChangeBoundsToolResizeFeedbackAction implements Action { | ||
export declare class FeedbackMoveMouseListener extends MouseListener { | ||
protected movementRestrictor?: IMovementRestrictor | undefined; | ||
protected tool: ChangeBoundsTool; | ||
hasDragged: boolean; | ||
lastDragPosition: Point | undefined; | ||
constructor(movementRestrictor?: IMovementRestrictor | undefined); | ||
startDragPosition: Point | undefined; | ||
elementId2startPos: Map<string, Point>; | ||
constructor(tool: ChangeBoundsTool); | ||
mouseDown(target: SModelElement, event: MouseEvent): Action[]; | ||
mouseMove(target: SModelElement, event: MouseEvent): Action[]; | ||
protected collectStartPositions(root: SModelRoot): void; | ||
protected getElementMoves(target: SModelElement, event: MouseEvent, isFinished: boolean): MoveAction | undefined; | ||
protected validateMove(startPostion: Point, toPosition: Point, element: SModelElement, isFinished: boolean): Point; | ||
protected snap(position: Point, element: SModelElement, isSnap: boolean): Point; | ||
mouseEnter(target: SModelElement, event: MouseEvent): Action[]; | ||
@@ -42,0 +47,0 @@ mouseUp(target: SModelElement, event: MouseEvent): Action[]; |
@@ -27,2 +27,22 @@ "use strict"; | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), r, ar = [], e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} | ||
catch (error) { e = { error: error }; } | ||
finally { | ||
try { | ||
if (r && !r.done && (m = i["return"])) m.call(i); | ||
} | ||
finally { if (e) throw e.error; } | ||
} | ||
return ar; | ||
}; | ||
var __spread = (this && this.__spread) || function () { | ||
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); | ||
return ar; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -47,4 +67,4 @@ /******************************************************************************** | ||
var smodel_util_1 = require("../../utils/smodel-util"); | ||
var viewpoint_util_1 = require("../../utils/viewpoint-util"); | ||
var model_1 = require("../change-bounds/model"); | ||
var movement_restrictor_1 = require("../change-bounds/movement-restrictor"); | ||
var model_2 = require("./model"); | ||
@@ -75,3 +95,6 @@ var ShowChangeBoundsToolResizeFeedbackAction = /** @class */ (function () { | ||
var index = context.root.index; | ||
index.all().filter(model_1.isResizable).forEach(model_1.removeResizeHandles); | ||
index | ||
.all() | ||
.filter(model_1.isResizable) | ||
.forEach(model_1.removeResizeHandles); | ||
if (smodel_util_1.isNotUndefined(this.action.elementId)) { | ||
@@ -85,3 +108,3 @@ var resizeElement = index.getById(this.action.elementId); | ||
}; | ||
ShowChangeBoundsToolResizeFeedbackCommand.KIND = 'showChangeBoundsToolResizeFeedback'; | ||
ShowChangeBoundsToolResizeFeedbackCommand.KIND = "showChangeBoundsToolResizeFeedback"; | ||
ShowChangeBoundsToolResizeFeedbackCommand = __decorate([ | ||
@@ -104,6 +127,9 @@ inversify_1.injectable(), | ||
var index = context.root.index; | ||
index.all().filter(model_1.isResizable).forEach(model_1.removeResizeHandles); | ||
index | ||
.all() | ||
.filter(model_1.isResizable) | ||
.forEach(model_1.removeResizeHandles); | ||
return context.root; | ||
}; | ||
HideChangeBoundsToolResizeFeedbackCommand.KIND = 'hideChangeBoundsToolResizeFeedback'; | ||
HideChangeBoundsToolResizeFeedbackCommand.KIND = "hideChangeBoundsToolResizeFeedback"; | ||
HideChangeBoundsToolResizeFeedbackCommand = __decorate([ | ||
@@ -126,16 +152,17 @@ inversify_1.injectable(), | ||
__extends(FeedbackMoveMouseListener, _super); | ||
function FeedbackMoveMouseListener(movementRestrictor) { | ||
function FeedbackMoveMouseListener(tool) { | ||
var _this = _super.call(this) || this; | ||
_this.movementRestrictor = movementRestrictor; | ||
_this.tool = tool; | ||
_this.hasDragged = false; | ||
_this.elementId2startPos = new Map(); | ||
return _this; | ||
} | ||
FeedbackMoveMouseListener.prototype.mouseDown = function (target, event) { | ||
if (event.button === 0) { | ||
if (event.button === 0 && !(target instanceof model_1.SResizeHandle)) { | ||
var moveable = lib_1.findParentByFeature(target, lib_1.isMoveable); | ||
if (moveable !== undefined) { | ||
this.lastDragPosition = { x: event.pageX, y: event.pageY }; | ||
this.startDragPosition = { x: event.pageX, y: event.pageY }; | ||
} | ||
else { | ||
this.lastDragPosition = undefined; | ||
this.startDragPosition = undefined; | ||
} | ||
@@ -147,26 +174,48 @@ this.hasDragged = false; | ||
FeedbackMoveMouseListener.prototype.mouseMove = function (target, event) { | ||
var _this = this; | ||
var result = []; | ||
if (event.buttons === 0) | ||
this.mouseUp(target, event); | ||
else if (this.lastDragPosition) { | ||
var viewport = lib_1.findParentByFeature(target, lib_1.isViewport); | ||
else if (this.startDragPosition) { | ||
if (this.elementId2startPos.size === 0) { | ||
this.collectStartPositions(target.root); | ||
} | ||
this.hasDragged = true; | ||
var zoom = viewport ? viewport.zoom : 1; | ||
var mousePoint_1 = viewpoint_util_1.getAbsolutePosition(target, event); | ||
var dx_1 = (event.pageX - this.lastDragPosition.x) / zoom; | ||
var dy_1 = (event.pageY - this.lastDragPosition.y) / zoom; | ||
var nodeMoves_1 = []; | ||
var isValidMove_1 = true; | ||
target.root.index.all() | ||
.filter(function (element) { return lib_1.isSelectable(element) && element.selected; }) | ||
.forEach(function (element) { | ||
if (model_1.isBoundsAwareMoveable(element)) { | ||
// If a movement restrictor is bound attemt a non restricted move | ||
if (_this.movementRestrictor) { | ||
isValidMove_1 = _this.movementRestrictor.attemptMove(element, mousePoint_1, target, { x: dx_1, y: dy_1 }, result); | ||
} | ||
} | ||
if (lib_1.isMoveable(element) && isValidMove_1) { | ||
nodeMoves_1.push({ | ||
var moveAction = this.getElementMoves(target, event, false); | ||
if (moveAction) | ||
result.push(moveAction); | ||
} | ||
return result; | ||
}; | ||
FeedbackMoveMouseListener.prototype.collectStartPositions = function (root) { | ||
var _this = this; | ||
root.index | ||
.all() | ||
.filter(function (element) { return lib_1.isSelectable(element) && element.selected; }) | ||
.forEach(function (element) { | ||
if (lib_1.isMoveable(element)) { | ||
_this.elementId2startPos.set(element.id, element.position); | ||
} | ||
}); | ||
}; | ||
FeedbackMoveMouseListener.prototype.getElementMoves = function (target, event, isFinished) { | ||
var _this = this; | ||
if (!this.startDragPosition) | ||
return undefined; | ||
var elementMoves = []; | ||
var viewport = lib_1.findParentByFeature(target, lib_1.isViewport); | ||
var zoom = viewport ? viewport.zoom : 1; | ||
var delta = { | ||
x: (event.pageX - this.startDragPosition.x) / zoom, | ||
y: (event.pageY - this.startDragPosition.y) / zoom | ||
}; | ||
this.elementId2startPos.forEach(function (startPosition, elementId) { | ||
var element = target.root.index.getById(elementId); | ||
if (element) { | ||
var toPosition = _this.snap({ | ||
x: startPosition.x + delta.x, | ||
y: startPosition.y + delta.y | ||
}, element, !event.shiftKey); | ||
if (lib_1.isMoveable(element)) { | ||
toPosition = _this.validateMove(startPosition, toPosition, element, isFinished); | ||
elementMoves.push({ | ||
elementId: element.id, | ||
@@ -177,16 +226,36 @@ fromPosition: { | ||
}, | ||
toPosition: { | ||
x: element.position.x + dx_1, | ||
y: element.position.y + dy_1 | ||
} | ||
toPosition: toPosition | ||
}); | ||
} | ||
}); | ||
this.lastDragPosition = { x: event.pageX, y: event.pageY }; | ||
if (nodeMoves_1.length > 0 && isValidMove_1) { | ||
result.push(new lib_1.MoveAction(nodeMoves_1, false)); | ||
} | ||
}); | ||
if (elementMoves.length > 0) | ||
return new lib_1.MoveAction(elementMoves, false, isFinished); | ||
else | ||
return undefined; | ||
}; | ||
FeedbackMoveMouseListener.prototype.validateMove = function (startPostion, toPosition, element, isFinished) { | ||
var newPosition = toPosition; | ||
if (this.tool.movementRestrictor) { | ||
var valid = this.tool.movementRestrictor.validate(toPosition, element); | ||
var actions = void 0; | ||
if (!valid) { | ||
actions = movement_restrictor_1.createMovementRestrictionFeedback(element, this.tool.movementRestrictor); | ||
if (isFinished) { | ||
newPosition = startPostion; | ||
} | ||
} | ||
else { | ||
actions = movement_restrictor_1.removeMovementRestrictionFeedback(element, this.tool.movementRestrictor); | ||
} | ||
this.tool.dispatchFeedback(this, actions); | ||
} | ||
return result; | ||
return newPosition; | ||
}; | ||
FeedbackMoveMouseListener.prototype.snap = function (position, element, isSnap) { | ||
if (isSnap && this.tool.snapper) | ||
return this.tool.snapper.snap(position, element); | ||
else | ||
return position; | ||
}; | ||
FeedbackMoveMouseListener.prototype.mouseEnter = function (target, event) { | ||
@@ -198,5 +267,16 @@ if (target instanceof lib_1.SModelRoot && event.buttons === 0) | ||
FeedbackMoveMouseListener.prototype.mouseUp = function (target, event) { | ||
var result = []; | ||
if (this.startDragPosition) { | ||
var moveAction = this.getElementMoves(target, event, true); | ||
if (moveAction) { | ||
result.push(moveAction); | ||
} | ||
if (this.tool.movementRestrictor) { | ||
result.push.apply(result, __spread(movement_restrictor_1.removeMovementRestrictionFeedback(target, this.tool.movementRestrictor))); | ||
} | ||
} | ||
this.hasDragged = false; | ||
this.lastDragPosition = undefined; | ||
return []; | ||
this.startDragPosition = undefined; | ||
this.elementId2startPos.clear(); | ||
return result; | ||
}; | ||
@@ -203,0 +283,0 @@ FeedbackMoveMouseListener.prototype.decorate = function (vnode, element) { |
@@ -170,3 +170,3 @@ "use strict"; | ||
var feedbackEdgeSchema = { | ||
type: 'edge', | ||
type: elementTypeId, | ||
id: feedbackEdgeId(root), | ||
@@ -173,0 +173,0 @@ sourceId: source.id, |
@@ -24,3 +24,3 @@ "use strict"; | ||
var creation_tool_feedback_1 = require("./creation-tool-feedback"); | ||
var cursor_feedback_1 = require("./cursor-feedback"); | ||
var css_feedback_1 = require("./css-feedback"); | ||
var edge_edit_tool_feedback_1 = require("./edge-edit-tool-feedback"); | ||
@@ -31,4 +31,4 @@ var feedback_action_dispatcher_1 = require("./feedback-action-dispatcher"); | ||
bind(types_1.GLSP_TYPES.IFeedbackActionDispatcher).to(feedback_action_dispatcher_1.FeedbackActionDispatcher).inSingletonScope(); | ||
lib_1.configureCommand({ bind: bind, isBound: isBound }, css_feedback_1.ModifyCssFeedbackCommand); | ||
// create node and edge tool feedback | ||
lib_1.configureCommand({ bind: bind, isBound: isBound }, cursor_feedback_1.ApplyCursorCSSFeedbackActionCommand); | ||
lib_1.configureCommand({ bind: bind, isBound: isBound }, creation_tool_feedback_1.DrawFeedbackEdgeCommand); | ||
@@ -35,0 +35,0 @@ lib_1.configureCommand({ bind: bind, isBound: isBound }, creation_tool_feedback_1.RemoveFeedbackEdgeCommand); |
@@ -256,3 +256,3 @@ "use strict"; | ||
target.root.index.all() | ||
.filter(function (element) { return smodel_util_1.isSelected(element); }) | ||
.filter(function (element) { return lib_1.isSelected(element); }) | ||
.forEach(function (element) { | ||
@@ -259,0 +259,0 @@ if (smodel_util_1.isRoutingHandle(element)) { |
@@ -1,2 +0,2 @@ | ||
import { Action, BoundsAware, KeyTool, MouseListener, Point, SModelElement, SModelRoot, SParentElement, Tool } from "sprotty/lib"; | ||
import { Action, Bounds, BoundsAware, Dimension, EdgeRouterRegistry, ElementAndBounds, ISnapper, KeyTool, ModelLayoutOptions, MouseListener, Point, SModelElement, SModelRoot, SParentElement, Tool } from "sprotty/lib"; | ||
import { SResizeHandle } from "../change-bounds/model"; | ||
@@ -6,4 +6,4 @@ import { IMovementRestrictor } from "../change-bounds/movement-restrictor"; | ||
import { SelectionListener, SelectionService } from "../select/selection-service"; | ||
import { FeedbackMoveMouseListener } from "../tool-feedback/change-bounds-tool-feedback"; | ||
import { IFeedbackActionDispatcher } from "../tool-feedback/feedback-action-dispatcher"; | ||
import { IFeedbackActionDispatcher, IFeedbackEmitter } from "../tool-feedback/feedback-action-dispatcher"; | ||
import { DragAwareMouseListener } from "./drag-aware-mouse-listener"; | ||
/** | ||
@@ -27,25 +27,31 @@ * The change bounds tool has the license to move multiple elements or resize a single element by implementing the ChangeBounds operation. | ||
protected feedbackDispatcher: IFeedbackActionDispatcher; | ||
protected movementRestrictor?: IMovementRestrictor | undefined; | ||
readonly edgeRouterRegistry?: EdgeRouterRegistry | undefined; | ||
readonly snapper?: ISnapper | undefined; | ||
readonly movementRestrictor?: IMovementRestrictor | undefined; | ||
static ID: string; | ||
readonly id: string; | ||
protected feedbackMoveMouseListener: FeedbackMoveMouseListener; | ||
protected changeBoundsListener: ChangeBoundsListener; | ||
constructor(selectionService: SelectionService, mouseTool: IMouseTool, keyTool: KeyTool, feedbackDispatcher: IFeedbackActionDispatcher, movementRestrictor?: IMovementRestrictor | undefined); | ||
protected feedbackMoveMouseListener: MouseListener; | ||
protected changeBoundsListener: MouseListener & SelectionListener; | ||
constructor(selectionService: SelectionService, mouseTool: IMouseTool, keyTool: KeyTool, feedbackDispatcher: IFeedbackActionDispatcher, edgeRouterRegistry?: EdgeRouterRegistry | undefined, snapper?: ISnapper | undefined, movementRestrictor?: IMovementRestrictor | undefined); | ||
enable(): void; | ||
protected createMoveMouseListener(): MouseListener; | ||
protected createChangeBoundsListener(): MouseListener & SelectionListener; | ||
disable(): void; | ||
dispatchFeedback(actions: Action[]): void; | ||
dispatchFeedback(feedbackEmmmiter: IFeedbackEmitter, actions: Action[]): void; | ||
} | ||
declare class ChangeBoundsListener extends MouseListener implements SelectionListener { | ||
export declare class ChangeBoundsListener extends DragAwareMouseListener implements SelectionListener { | ||
protected tool: ChangeBoundsTool; | ||
protected lastDragPosition: Point | undefined; | ||
protected lastDragPosition?: Point; | ||
protected positionDelta: Point; | ||
protected activeResizeElementId: string | undefined; | ||
protected activeResizeHandle: SResizeHandle | undefined; | ||
protected initialPositon: Point | undefined; | ||
protected initialBounds: Bounds | undefined; | ||
protected activeResizeElementId?: string; | ||
protected activeResizeHandle?: SResizeHandle; | ||
constructor(tool: ChangeBoundsTool); | ||
mouseDown(target: SModelElement, event: MouseEvent): Action[]; | ||
mouseMove(target: SModelElement, event: MouseEvent): Action[]; | ||
mouseUp(target: SModelElement, event: MouseEvent): Action[]; | ||
draggingMouseUp(target: SModelElement, event: MouseEvent): Action[]; | ||
selectionChanged(root: SModelRoot, selectedElements: string[]): void; | ||
protected setActiveResizeElement(target: SModelElement): boolean; | ||
protected isActiveResizeElement(element: SModelElement | undefined): element is SParentElement & BoundsAware; | ||
protected isActiveResizeElement(element?: SModelElement): element is SParentElement & BoundsAware; | ||
protected initPosition(event: MouseEvent): void; | ||
@@ -55,6 +61,12 @@ protected updatePosition(target: SModelElement, event: MouseEvent): boolean; | ||
protected resetPosition(): void; | ||
protected hasPositionDelta(): boolean; | ||
protected handleElementResize(): Action[]; | ||
protected createChangeBoundsAction(element: SModelElement & BoundsAware): Action[]; | ||
protected createElementAndBounds(element: SModelElement & BoundsAware): ElementAndBounds[]; | ||
protected createSetBoundsAction(element: SModelElement & BoundsAware, x: number, y: number, width: number, height: number): Action[]; | ||
protected isValidBoundChange(element: SModelElement & BoundsAware, newPosition: Point, newSize: Dimension): boolean; | ||
protected isValidSize(element: SModelElement & BoundsAware, size: Dimension): boolean; | ||
protected minWidth(element: SModelElement & BoundsAware): number; | ||
protected minHeight(element: SModelElement & BoundsAware): number; | ||
protected getLayoutOptions(element: SModelElement): ModelLayoutOptions | undefined; | ||
} | ||
export {}; | ||
//# sourceMappingURL=change-bounds-tool.d.ts.map |
@@ -38,2 +38,22 @@ "use strict"; | ||
}; | ||
var __read = (this && this.__read) || function (o, n) { | ||
var m = typeof Symbol === "function" && o[Symbol.iterator]; | ||
if (!m) return o; | ||
var i = m.call(o), r, ar = [], e; | ||
try { | ||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); | ||
} | ||
catch (error) { e = { error: error }; } | ||
finally { | ||
try { | ||
if (r && !r.done && (m = i["return"])) m.call(i); | ||
} | ||
finally { if (e) throw e.error; } | ||
} | ||
return ar; | ||
}; | ||
var __spread = (this && this.__spread) || function () { | ||
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); | ||
return ar; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -60,5 +80,7 @@ /******************************************************************************** | ||
var model_1 = require("../change-bounds/model"); | ||
var movement_restrictor_1 = require("../change-bounds/movement-restrictor"); | ||
var operation_actions_1 = require("../operation/operation-actions"); | ||
var selection_service_1 = require("../select/selection-service"); | ||
var change_bounds_tool_feedback_1 = require("../tool-feedback/change-bounds-tool-feedback"); | ||
var drag_aware_mouse_listener_1 = require("./drag-aware-mouse-listener"); | ||
/** | ||
@@ -78,3 +100,3 @@ * The change bounds tool has the license to move multiple elements or resize a single element by implementing the ChangeBounds operation. | ||
var ChangeBoundsTool = /** @class */ (function () { | ||
function ChangeBoundsTool(selectionService, mouseTool, keyTool, feedbackDispatcher, movementRestrictor) { | ||
function ChangeBoundsTool(selectionService, mouseTool, keyTool, feedbackDispatcher, edgeRouterRegistry, snapper, movementRestrictor) { | ||
this.selectionService = selectionService; | ||
@@ -84,2 +106,4 @@ this.mouseTool = mouseTool; | ||
this.feedbackDispatcher = feedbackDispatcher; | ||
this.edgeRouterRegistry = edgeRouterRegistry; | ||
this.snapper = snapper; | ||
this.movementRestrictor = movementRestrictor; | ||
@@ -91,10 +115,17 @@ this.id = ChangeBoundsTool_1.ID; | ||
// install feedback move mouse listener for client-side move updates | ||
this.feedbackMoveMouseListener = new change_bounds_tool_feedback_1.FeedbackMoveMouseListener(this.movementRestrictor); | ||
this.feedbackMoveMouseListener = this.createMoveMouseListener(); | ||
this.mouseTool.register(this.feedbackMoveMouseListener); | ||
// instlal change bounds listener for client-side resize updates and server-side updates | ||
this.changeBoundsListener = new ChangeBoundsListener(this); | ||
// install change bounds listener for client-side resize updates and server-side updates | ||
this.changeBoundsListener = this.createChangeBoundsListener(); | ||
this.mouseTool.register(this.changeBoundsListener); | ||
this.selectionService.register(this.changeBoundsListener); | ||
// register feedback | ||
this.feedbackDispatcher.registerFeedback(this, [new change_bounds_tool_feedback_1.ShowChangeBoundsToolResizeFeedbackAction]); | ||
}; | ||
ChangeBoundsTool.prototype.createMoveMouseListener = function () { | ||
return new change_bounds_tool_feedback_1.FeedbackMoveMouseListener(this); | ||
}; | ||
ChangeBoundsTool.prototype.createChangeBoundsListener = function () { | ||
return new ChangeBoundsListener(this); | ||
}; | ||
ChangeBoundsTool.prototype.disable = function () { | ||
@@ -104,6 +135,7 @@ this.mouseTool.deregister(this.changeBoundsListener); | ||
this.mouseTool.deregister(this.feedbackMoveMouseListener); | ||
this.feedbackDispatcher.deregisterFeedback(this, [new change_bounds_tool_feedback_1.HideChangeBoundsToolResizeFeedbackAction]); | ||
this.feedbackDispatcher.deregisterFeedback(this.feedbackMoveMouseListener, []); | ||
this.feedbackDispatcher.deregisterFeedback(this.changeBoundsListener, [new change_bounds_tool_feedback_1.HideChangeBoundsToolResizeFeedbackAction]); | ||
}; | ||
ChangeBoundsTool.prototype.dispatchFeedback = function (actions) { | ||
this.feedbackDispatcher.registerFeedback(this, actions); | ||
ChangeBoundsTool.prototype.dispatchFeedback = function (feedbackEmmmiter, actions) { | ||
this.feedbackDispatcher.registerFeedback(feedbackEmmmiter, actions); | ||
}; | ||
@@ -118,4 +150,6 @@ var ChangeBoundsTool_1; | ||
__param(3, inversify_1.inject(types_1.GLSP_TYPES.IFeedbackActionDispatcher)), | ||
__param(4, inversify_1.inject(types_1.GLSP_TYPES.IMovementRestrictor)), __param(4, inversify_1.optional()), | ||
__metadata("design:paramtypes", [selection_service_1.SelectionService, Object, lib_1.KeyTool, Object, Object]) | ||
__param(4, inversify_1.inject(lib_1.EdgeRouterRegistry)), __param(4, inversify_1.optional()), | ||
__param(5, inversify_1.inject(lib_1.TYPES.ISnapper)), __param(5, inversify_1.optional()), | ||
__param(6, inversify_1.inject(types_1.GLSP_TYPES.IMovementRestrictor)), __param(6, inversify_1.optional()), | ||
__metadata("design:paramtypes", [selection_service_1.SelectionService, Object, lib_1.KeyTool, Object, lib_1.EdgeRouterRegistry, Object, Object]) | ||
], ChangeBoundsTool); | ||
@@ -130,8 +164,4 @@ return ChangeBoundsTool; | ||
_this.tool = tool; | ||
// members for calculating the correct position change | ||
_this.lastDragPosition = undefined; | ||
_this.positionDelta = { x: 0, y: 0 }; | ||
// members for resize mode | ||
_this.activeResizeElementId = undefined; | ||
_this.activeResizeHandle = undefined; | ||
_this.initialPositon = undefined; | ||
return _this; | ||
@@ -141,22 +171,23 @@ } | ||
_super.prototype.mouseDown.call(this, target, event); | ||
var actions = []; | ||
if (event.button === 0) { | ||
// check if we have a resize handle (only single-selection) | ||
if (this.activeResizeElementId && target instanceof model_1.SResizeHandle) { | ||
this.activeResizeHandle = target; | ||
} | ||
else { | ||
this.setActiveResizeElement(target); | ||
} | ||
if (this.activeResizeElementId) { | ||
this.initPosition(event); | ||
} | ||
else { | ||
this.reset(); | ||
} | ||
if (event.button !== 0) { | ||
return []; | ||
} | ||
return actions; | ||
// check if we have a resize handle (only single-selection) | ||
if (this.activeResizeElementId && target instanceof model_1.SResizeHandle) { | ||
this.activeResizeHandle = target; | ||
} | ||
else { | ||
this.setActiveResizeElement(target); | ||
} | ||
if (this.activeResizeElementId) { | ||
this.initPosition(event); | ||
} | ||
else { | ||
this.reset(); | ||
} | ||
return []; | ||
}; | ||
ChangeBoundsListener.prototype.mouseMove = function (target, event) { | ||
if (this.updatePosition(target, event)) { | ||
_super.prototype.mouseMove.call(this, target, event); | ||
if (this.updatePosition(target, event) && this.activeResizeHandle) { | ||
// rely on the FeedbackMoveMouseListener to update the element bounds of selected elements | ||
@@ -168,22 +199,27 @@ // consider resize handles ourselves | ||
}; | ||
ChangeBoundsListener.prototype.mouseUp = function (target, event) { | ||
_super.prototype.mouseUp.call(this, target, event); | ||
if (!this.hasPositionDelta()) { | ||
ChangeBoundsListener.prototype.draggingMouseUp = function (target, event) { | ||
var _this = this; | ||
if (this.lastDragPosition === undefined) { | ||
this.resetPosition(); | ||
return []; | ||
} | ||
// no further bound changing, simply send the latest data to the server using a single change bounds action for all relevant elements | ||
var actions = []; | ||
if (this.activeResizeHandle) { | ||
// An action. Resize, not move. | ||
// Resize, not move | ||
var resizeElement = lib_1.findParentByFeature(this.activeResizeHandle, model_1.isResizable); | ||
if (this.isActiveResizeElement(resizeElement)) { | ||
createChangeBoundsAction(resizeElement).forEach(function (action) { return actions.push(action); }); | ||
this.createChangeBoundsAction(resizeElement).forEach(function (action) { return actions.push(action); }); | ||
} | ||
} | ||
else { | ||
// Bounds... Change Bounds. | ||
// Move | ||
var newBounds_1 = []; | ||
smodel_util_1.forEachElement(target, smodel_util_1.isNonRoutableSelectedBoundsAware, function (element) { | ||
return createElementAndBounds(element).forEach(function (bounds) { return newBounds_1.push(bounds); }); | ||
var newRoutingPoints_1 = []; | ||
smodel_util_1.forEachElement(target, smodel_util_1.isNonRoutableSelectedMovableBoundsAware, function (element) { | ||
_this.createElementAndBounds(element).forEach(function (bounds) { return newBounds_1.push(bounds); }); | ||
// If client routing is enabled -> delegate routingpoints of connected edges to server | ||
if (_this.tool.edgeRouterRegistry && element instanceof lib_1.SConnectableElement) { | ||
element.incomingEdges.map(smodel_util_1.toElementAndRoutingPoints).forEach(function (ear) { return newRoutingPoints_1.push(ear); }); | ||
element.outgoingEdges.map(smodel_util_1.toElementAndRoutingPoints).forEach(function (ear) { return newRoutingPoints_1.push(ear); }); | ||
} | ||
}); | ||
@@ -193,2 +229,5 @@ if (newBounds_1.length > 0) { | ||
} | ||
if (newRoutingPoints_1.length > 0) { | ||
actions.push(new operation_actions_1.ChangeRoutingPointsOperation(newRoutingPoints_1)); | ||
} | ||
} | ||
@@ -201,3 +240,3 @@ this.resetPosition(); | ||
if (this.activeResizeElementId) { | ||
if (selectedElements.indexOf(this.activeResizeElementId) > -1) { | ||
if (selectedElements.includes(this.activeResizeElementId)) { | ||
// our active element is still selected, nothing to do | ||
@@ -229,6 +268,6 @@ return; | ||
var moveableElement = lib_1.findParentByFeature(target, model_1.isBoundsAwareMoveable); | ||
if (smodel_util_1.isSelected(moveableElement)) { | ||
if (lib_1.isSelected(moveableElement)) { | ||
// only allow one element to have the element resize handles | ||
this.activeResizeElementId = moveableElement.id; | ||
this.tool.dispatchFeedback([new change_bounds_tool_feedback_1.ShowChangeBoundsToolResizeFeedbackAction(this.activeResizeElementId)]); | ||
this.tool.dispatchFeedback(this, [new change_bounds_tool_feedback_1.ShowChangeBoundsToolResizeFeedbackAction(this.activeResizeElementId)]); | ||
return true; | ||
@@ -242,3 +281,8 @@ } | ||
ChangeBoundsListener.prototype.initPosition = function (event) { | ||
this.initialPositon = { x: event.pageX, y: event.pageY }; | ||
this.lastDragPosition = { x: event.pageX, y: event.pageY }; | ||
if (this.activeResizeHandle) { | ||
var resizeElement = lib_1.findParentByFeature(this.activeResizeHandle, model_1.isResizable); | ||
this.initialBounds = { x: resizeElement.bounds.x, y: resizeElement.bounds.y, width: resizeElement.bounds.width, height: resizeElement.bounds.height }; | ||
} | ||
}; | ||
@@ -258,3 +302,3 @@ ChangeBoundsListener.prototype.updatePosition = function (target, event) { | ||
ChangeBoundsListener.prototype.reset = function () { | ||
this.tool.dispatchFeedback([new change_bounds_tool_feedback_1.HideChangeBoundsToolResizeFeedbackAction()]); | ||
this.tool.dispatchFeedback(this, [new change_bounds_tool_feedback_1.HideChangeBoundsToolResizeFeedbackAction()]); | ||
this.resetPosition(); | ||
@@ -265,7 +309,5 @@ }; | ||
this.lastDragPosition = undefined; | ||
this.initialPositon = undefined; | ||
this.positionDelta = { x: 0, y: 0 }; | ||
}; | ||
ChangeBoundsListener.prototype.hasPositionDelta = function () { | ||
return this.positionDelta.x !== 0 || this.positionDelta.y !== 0; | ||
}; | ||
ChangeBoundsListener.prototype.handleElementResize = function () { | ||
@@ -280,15 +322,15 @@ if (!this.activeResizeHandle) { | ||
case model_1.ResizeHandleLocation.TopLeft: | ||
createSetBoundsAction(resizeElement, resizeElement.bounds.x + this.positionDelta.x, resizeElement.bounds.y + this.positionDelta.y, resizeElement.bounds.width - this.positionDelta.x, resizeElement.bounds.height - this.positionDelta.y) | ||
this.createSetBoundsAction(resizeElement, resizeElement.bounds.x + this.positionDelta.x, resizeElement.bounds.y + this.positionDelta.y, resizeElement.bounds.width - this.positionDelta.x, resizeElement.bounds.height - this.positionDelta.y) | ||
.forEach(function (action) { return actions.push(action); }); | ||
break; | ||
case model_1.ResizeHandleLocation.TopRight: | ||
createSetBoundsAction(resizeElement, resizeElement.bounds.x, resizeElement.bounds.y + this.positionDelta.y, resizeElement.bounds.width + this.positionDelta.x, resizeElement.bounds.height - this.positionDelta.y) | ||
this.createSetBoundsAction(resizeElement, resizeElement.bounds.x, resizeElement.bounds.y + this.positionDelta.y, resizeElement.bounds.width + this.positionDelta.x, resizeElement.bounds.height - this.positionDelta.y) | ||
.forEach(function (action) { return actions.push(action); }); | ||
break; | ||
case model_1.ResizeHandleLocation.BottomLeft: | ||
createSetBoundsAction(resizeElement, resizeElement.bounds.x + this.positionDelta.x, resizeElement.bounds.y, resizeElement.bounds.width - this.positionDelta.x, resizeElement.bounds.height + this.positionDelta.y) | ||
this.createSetBoundsAction(resizeElement, resizeElement.bounds.x + this.positionDelta.x, resizeElement.bounds.y, resizeElement.bounds.width - this.positionDelta.x, resizeElement.bounds.height + this.positionDelta.y) | ||
.forEach(function (action) { return actions.push(action); }); | ||
break; | ||
case model_1.ResizeHandleLocation.BottomRight: | ||
createSetBoundsAction(resizeElement, resizeElement.bounds.x, resizeElement.bounds.y, resizeElement.bounds.width + this.positionDelta.x, resizeElement.bounds.height + this.positionDelta.y) | ||
this.createSetBoundsAction(resizeElement, resizeElement.bounds.x, resizeElement.bounds.y, resizeElement.bounds.width + this.positionDelta.x, resizeElement.bounds.height + this.positionDelta.y) | ||
.forEach(function (action) { return actions.push(action); }); | ||
@@ -300,35 +342,74 @@ break; | ||
}; | ||
ChangeBoundsListener.prototype.createChangeBoundsAction = function (element) { | ||
if (this.isValidBoundChange(element, element.bounds, element.bounds)) { | ||
return [new operation_actions_1.ChangeBoundsOperationAction([smodel_util_1.toElementAndBounds(element)])]; | ||
} | ||
else if (this.initialBounds) { | ||
var actions = []; | ||
if (this.tool.movementRestrictor) { | ||
actions.push.apply(actions, __spread(movement_restrictor_1.removeMovementRestrictionFeedback(element, this.tool.movementRestrictor))); | ||
} | ||
actions.push(new lib_1.SetBoundsAction([{ elementId: element.id, newPosition: this.initialBounds, newSize: this.initialBounds }])); | ||
return actions; | ||
} | ||
return []; | ||
}; | ||
ChangeBoundsListener.prototype.createElementAndBounds = function (element) { | ||
if (this.isValidBoundChange(element, element.bounds, element.bounds)) { | ||
return [smodel_util_1.toElementAndBounds(element)]; | ||
} | ||
return []; | ||
}; | ||
ChangeBoundsListener.prototype.createSetBoundsAction = function (element, x, y, width, height) { | ||
var newPosition = { x: x, y: y }; | ||
var newSize = { width: width, height: height }; | ||
var result = []; | ||
if (this.isValidBoundChange(element, newPosition, newSize)) { | ||
if (this.tool.movementRestrictor) { | ||
result.push.apply(result, __spread(movement_restrictor_1.removeMovementRestrictionFeedback(element, this.tool.movementRestrictor))); | ||
} | ||
result.push(new lib_1.SetBoundsAction([{ elementId: element.id, newPosition: newPosition, newSize: newSize }])); | ||
} | ||
else if (this.isValidSize(element, newSize)) { | ||
if (this.tool.movementRestrictor) { | ||
result.push.apply(result, __spread(movement_restrictor_1.createMovementRestrictionFeedback(element, this.tool.movementRestrictor))); | ||
} | ||
result.push(new lib_1.SetBoundsAction([{ elementId: element.id, newPosition: newPosition, newSize: newSize }])); | ||
} | ||
return result; | ||
}; | ||
ChangeBoundsListener.prototype.isValidBoundChange = function (element, newPosition, newSize) { | ||
var valid = this.isValidSize(element, newSize); | ||
if (this.tool.movementRestrictor) { | ||
return valid && this.tool.movementRestrictor.validate(newPosition, element); | ||
} | ||
return valid; | ||
}; | ||
ChangeBoundsListener.prototype.isValidSize = function (element, size) { | ||
return size.width >= this.minWidth(element) && size.height >= this.minHeight(element); | ||
}; | ||
ChangeBoundsListener.prototype.minWidth = function (element) { | ||
var layoutOptions = this.getLayoutOptions(element); | ||
if (layoutOptions !== undefined && typeof layoutOptions.minWidth === 'number') { | ||
return layoutOptions.minWidth; | ||
} | ||
return 1; | ||
}; | ||
ChangeBoundsListener.prototype.minHeight = function (element) { | ||
var layoutOptions = this.getLayoutOptions(element); | ||
if (layoutOptions !== undefined && typeof layoutOptions.minHeight === 'number') { | ||
return layoutOptions.minHeight; | ||
} | ||
return 1; | ||
}; | ||
ChangeBoundsListener.prototype.getLayoutOptions = function (element) { | ||
var layoutOptions = element.layoutOptions; | ||
if (layoutOptions !== undefined) { | ||
return layoutOptions; | ||
} | ||
return undefined; | ||
}; | ||
return ChangeBoundsListener; | ||
}(lib_1.MouseListener)); | ||
function createChangeBoundsAction(element) { | ||
if (isValidBoundChange(element, element.bounds, element.bounds)) { | ||
return [new operation_actions_1.ChangeBoundsOperationAction([smodel_util_1.toElementAndBounds(element)])]; | ||
} | ||
return []; | ||
} | ||
function createElementAndBounds(element) { | ||
if (isValidBoundChange(element, element.bounds, element.bounds)) { | ||
return [smodel_util_1.toElementAndBounds(element)]; | ||
} | ||
return []; | ||
} | ||
function createSetBoundsAction(element, x, y, width, height) { | ||
var newPosition = { x: x, y: y }; | ||
var newSize = { width: width, height: height }; | ||
if (isValidBoundChange(element, newPosition, newSize)) { | ||
return [new lib_1.SetBoundsAction([{ elementId: element.id, newPosition: newPosition, newSize: newSize }])]; | ||
} | ||
return []; | ||
} | ||
function isValidBoundChange(element, newPosition, newSize) { | ||
return newSize.width >= minWidth(element) && newSize.height >= minHeight(element); | ||
} | ||
function minWidth(element) { | ||
// currently there are no element-specific constraints | ||
return 1; | ||
} | ||
function minHeight(element) { | ||
// currently there are no element-specific constraints | ||
return 1; | ||
} | ||
}(drag_aware_mouse_listener_1.DragAwareMouseListener)); | ||
exports.ChangeBoundsListener = ChangeBoundsListener; | ||
//# sourceMappingURL=change-bounds-tool.js.map |
@@ -51,3 +51,3 @@ "use strict"; | ||
var creation_tool_feedback_1 = require("../tool-feedback/creation-tool-feedback"); | ||
var cursor_feedback_1 = require("../tool-feedback/cursor-feedback"); | ||
var css_feedback_1 = require("../tool-feedback/css-feedback"); | ||
var drag_aware_mouse_listener_1 = require("./drag-aware-mouse-listener"); | ||
@@ -75,7 +75,7 @@ exports.TOOL_ID_PREFIX = "tool"; | ||
this.mouseTool.register(this.creationToolMouseListener); | ||
this.feedbackDispatcher.registerFeedback(this, [new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.NODE_CREATION)]); | ||
this.feedbackDispatcher.registerFeedback(this, [css_feedback_1.cursorFeedbackAction(css_feedback_1.CursorCSS.NODE_CREATION)]); | ||
}; | ||
NodeCreationTool.prototype.disable = function () { | ||
this.mouseTool.deregister(this.creationToolMouseListener); | ||
this.feedbackDispatcher.deregisterFeedback(this, [new cursor_feedback_1.ApplyCursorCSSFeedbackAction()]); | ||
this.feedbackDispatcher.deregisterFeedback(this, [css_feedback_1.cursorFeedbackAction()]); | ||
}; | ||
@@ -122,4 +122,4 @@ NodeCreationTool.prototype.dispatchFeedback = function (actions) { | ||
var feedback = this.creationAllowed(this.elementTypeId) | ||
? new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.NODE_CREATION) : | ||
new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.OPERATION_NOT_ALLOWED); | ||
? css_feedback_1.cursorFeedbackAction(css_feedback_1.CursorCSS.NODE_CREATION) : | ||
css_feedback_1.cursorFeedbackAction(css_feedback_1.CursorCSS.OPERATION_NOT_ALLOWED); | ||
this.tool.dispatchFeedback([feedback]); | ||
@@ -159,3 +159,3 @@ } | ||
this.mouseTool.register(this.feedbackEndMovingMouseListener); | ||
this.dispatchFeedback([new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.OPERATION_NOT_ALLOWED)]); | ||
this.dispatchFeedback([css_feedback_1.cursorFeedbackAction(css_feedback_1.CursorCSS.OPERATION_NOT_ALLOWED)]); | ||
}; | ||
@@ -165,3 +165,3 @@ EdgeCreationTool.prototype.disable = function () { | ||
this.mouseTool.deregister(this.feedbackEndMovingMouseListener); | ||
this.feedbackDispatcher.deregisterFeedback(this, [new creation_tool_feedback_1.RemoveFeedbackEdgeAction(), new cursor_feedback_1.ApplyCursorCSSFeedbackAction()]); | ||
this.feedbackDispatcher.deregisterFeedback(this, [new creation_tool_feedback_1.RemoveFeedbackEdgeAction(), css_feedback_1.cursorFeedbackAction()]); | ||
}; | ||
@@ -247,8 +247,8 @@ EdgeCreationTool.prototype.dispatchFeedback = function (actions) { | ||
if (this.allowedTarget) { | ||
var action = !this.isSourceSelected() ? new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.EDGE_CREATION_SOURCE) : | ||
new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.EDGE_CREATION_TARGET); | ||
var action = !this.isSourceSelected() ? css_feedback_1.cursorFeedbackAction(css_feedback_1.CursorCSS.EDGE_CREATION_SOURCE) : | ||
css_feedback_1.cursorFeedbackAction(css_feedback_1.CursorCSS.EDGE_CREATION_TARGET); | ||
return [action]; | ||
} | ||
} | ||
return [new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.OPERATION_NOT_ALLOWED)]; | ||
return [css_feedback_1.cursorFeedbackAction(css_feedback_1.CursorCSS.OPERATION_NOT_ALLOWED)]; | ||
} | ||
@@ -255,0 +255,0 @@ return []; |
@@ -48,3 +48,3 @@ "use strict"; | ||
var operation_actions_1 = require("../operation/operation-actions"); | ||
var cursor_feedback_1 = require("../tool-feedback/cursor-feedback"); | ||
var css_feedback_1 = require("../tool-feedback/css-feedback"); | ||
/** | ||
@@ -108,7 +108,7 @@ * Deletes selected elements when hitting the `Del` key. | ||
this.mouseTool.register(this.deleteToolMouseListener); | ||
this.feedbackDispatcher.registerFeedback(this, [new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.ELEMENT_DELETION)]); | ||
this.feedbackDispatcher.registerFeedback(this, [css_feedback_1.cursorFeedbackAction(css_feedback_1.CursorCSS.ELEMENT_DELETION)]); | ||
}; | ||
MouseDeleteTool.prototype.disable = function () { | ||
this.mouseTool.deregister(this.deleteToolMouseListener); | ||
this.feedbackDispatcher.registerFeedback(this, [new cursor_feedback_1.ApplyCursorCSSFeedbackAction()]); | ||
this.feedbackDispatcher.registerFeedback(this, [css_feedback_1.cursorFeedbackAction()]); | ||
}; | ||
@@ -115,0 +115,0 @@ var MouseDeleteTool_1; |
@@ -26,4 +26,4 @@ /******************************************************************************** | ||
export declare class DragAwareMouseListener extends MouseListener { | ||
private isMouseDown; | ||
private isMouseDrag; | ||
private _isMouseDown; | ||
private _isMouseDrag; | ||
mouseDown(target: SModelElement, event: MouseEvent): Action[]; | ||
@@ -34,4 +34,5 @@ mouseMove(target: SModelElement, event: MouseEvent): Action[]; | ||
draggingMouseUp(element: SModelElement, event: MouseEvent): Action[]; | ||
readonly isDragging: boolean; | ||
readonly isMouseDrag: boolean; | ||
readonly isMouseDown: boolean; | ||
} | ||
//# sourceMappingURL=drag-aware-mouse-listener.d.ts.map |
@@ -44,13 +44,13 @@ "use strict"; | ||
var _this = _super !== null && _super.apply(this, arguments) || this; | ||
_this.isMouseDown = false; | ||
_this.isMouseDrag = false; | ||
_this._isMouseDown = false; | ||
_this._isMouseDrag = false; | ||
return _this; | ||
} | ||
DragAwareMouseListener.prototype.mouseDown = function (target, event) { | ||
this.isMouseDown = true; | ||
this._isMouseDown = true; | ||
return []; | ||
}; | ||
DragAwareMouseListener.prototype.mouseMove = function (target, event) { | ||
if (this.isMouseDown) { | ||
this.isMouseDrag = true; | ||
if (this._isMouseDown) { | ||
this._isMouseDrag = true; | ||
} | ||
@@ -60,5 +60,5 @@ return []; | ||
DragAwareMouseListener.prototype.mouseUp = function (element, event) { | ||
this.isMouseDown = false; | ||
if (this.isMouseDrag) { | ||
this.isMouseDrag = false; | ||
this._isMouseDown = false; | ||
if (this._isMouseDrag) { | ||
this._isMouseDrag = false; | ||
return this.draggingMouseUp(element, event); | ||
@@ -74,5 +74,5 @@ } | ||
}; | ||
Object.defineProperty(DragAwareMouseListener.prototype, "isDragging", { | ||
Object.defineProperty(DragAwareMouseListener.prototype, "isMouseDrag", { | ||
get: function () { | ||
return this.isMouseDrag; | ||
return this._isMouseDrag; | ||
}, | ||
@@ -82,2 +82,9 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(DragAwareMouseListener.prototype, "isMouseDown", { | ||
get: function () { | ||
return this._isMouseDown; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
return DragAwareMouseListener; | ||
@@ -84,0 +91,0 @@ }(lib_1.MouseListener)); |
@@ -78,7 +78,7 @@ "use strict"; | ||
var smodel_util_1 = require("../../utils/smodel-util"); | ||
var action_definitions_1 = require("../reconnect/action-definitions"); | ||
var operation_actions_1 = require("../operation/operation-actions"); | ||
var model_1 = require("../reconnect/model"); | ||
var selection_service_1 = require("../select/selection-service"); | ||
var creation_tool_feedback_1 = require("../tool-feedback/creation-tool-feedback"); | ||
var cursor_feedback_1 = require("../tool-feedback/cursor-feedback"); | ||
var css_feedback_1 = require("../tool-feedback/css-feedback"); | ||
var edge_edit_tool_feedback_1 = require("../tool-feedback/edge-edit-tool-feedback"); | ||
@@ -141,3 +141,3 @@ var EdgeEditTool = /** @class */ (function () { | ||
ReconnectEdgeListener.prototype.isValidEdge = function (edge) { | ||
return edge !== undefined && edge.id !== creation_tool_feedback_1.feedbackEdgeId(edge.root) && smodel_util_1.isSelected(edge); | ||
return edge !== undefined && edge.id !== creation_tool_feedback_1.feedbackEdgeId(edge.root) && lib_1.isSelected(edge); | ||
}; | ||
@@ -161,3 +161,3 @@ ReconnectEdgeListener.prototype.setEdgeSelected = function (edge) { | ||
ReconnectEdgeListener.prototype.isEdgeSelected = function () { | ||
return this.edge !== undefined && smodel_util_1.isSelected(this.edge); | ||
return this.edge !== undefined && lib_1.isSelected(this.edge); | ||
}; | ||
@@ -168,3 +168,3 @@ ReconnectEdgeListener.prototype.setReconnectHandleSelected = function (edge, reconnectHandle) { | ||
this.tool.dispatchFeedback([new edge_edit_tool_feedback_1.HideEdgeReconnectHandlesFeedbackAction(), | ||
new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.EDGE_RECONNECT), | ||
css_feedback_1.cursorFeedbackAction(css_feedback_1.CursorCSS.EDGE_RECONNECT), | ||
new edge_edit_tool_feedback_1.DrawFeedbackEdgeSourceAction(this.edge.type, this.edge.targetId)]); | ||
@@ -175,3 +175,3 @@ this.reconnectMode = "NEW_SOURCE"; | ||
this.tool.dispatchFeedback([new edge_edit_tool_feedback_1.HideEdgeReconnectHandlesFeedbackAction(), | ||
new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.EDGE_CREATION_TARGET), | ||
css_feedback_1.cursorFeedbackAction(css_feedback_1.CursorCSS.EDGE_CREATION_TARGET), | ||
new creation_tool_feedback_1.DrawFeedbackEdgeAction(this.edge.type, this.edge.sourceId)]); | ||
@@ -247,3 +247,3 @@ this.reconnectMode = "NEW_TARGET"; | ||
if (this.requiresReconnect(sourceId, targetId)) { | ||
result.push(new action_definitions_1.ReconnectConnectionOperationAction(this.edge.id, sourceId, targetId)); | ||
result.push(new operation_actions_1.ReconnectConnectionOperationAction(this.edge.id, sourceId, targetId)); | ||
} | ||
@@ -256,3 +256,3 @@ this.reset(); | ||
if (latestEdge && smodel_util_1.isRoutable(latestEdge)) { | ||
result.push(new action_definitions_1.RerouteConnectionOperationAction(latestEdge.id, latestEdge.routingPoints)); | ||
result.push(new operation_actions_1.ChangeRoutingPointsOperation([{ elementId: latestEdge.id, newRoutingPoints: latestEdge.routingPoints }])); | ||
this.routingHandle = undefined; | ||
@@ -271,7 +271,7 @@ } | ||
(this.reconnectMode === 'NEW_TARGET' && currentTarget.canConnect(this.edge, "target"))) { | ||
this.tool.dispatchFeedback([new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.EDGE_RECONNECT)]); | ||
this.tool.dispatchFeedback([css_feedback_1.cursorFeedbackAction(css_feedback_1.CursorCSS.EDGE_RECONNECT)]); | ||
return []; | ||
} | ||
} | ||
this.tool.dispatchFeedback([new cursor_feedback_1.ApplyCursorCSSFeedbackAction(cursor_feedback_1.CursorCSS.OPERATION_NOT_ALLOWED)]); | ||
this.tool.dispatchFeedback([css_feedback_1.cursorFeedbackAction(css_feedback_1.CursorCSS.OPERATION_NOT_ALLOWED)]); | ||
} | ||
@@ -334,3 +334,3 @@ } | ||
result.push.apply(result, __spread([new edge_edit_tool_feedback_1.HideEdgeReconnectHandlesFeedbackAction(), | ||
new cursor_feedback_1.ApplyCursorCSSFeedbackAction(), new creation_tool_feedback_1.RemoveFeedbackEdgeAction()])); | ||
css_feedback_1.cursorFeedbackAction(), new creation_tool_feedback_1.RemoveFeedbackEdgeAction()])); | ||
this.tool.dispatchFeedback(result); | ||
@@ -337,0 +337,0 @@ }; |
@@ -36,7 +36,5 @@ /******************************************************************************** | ||
export * from './features/change-bounds/movement-restrictor'; | ||
export * from './features/change-bounds/snap'; | ||
export * from './features/context-actions/action-definitions'; | ||
export * from './features/command-palette/server-command-palette-provider'; | ||
export * from './features/context-menu/mouse-listener'; | ||
export * from './features/context-menu/context-menu-service'; | ||
export * from './features/context-menu/menu-providers'; | ||
export * from './features/edit-label-validation/edit-label-validator'; | ||
@@ -53,3 +51,2 @@ export * from './features/execute/execute-command'; | ||
export * from './features/rank/model'; | ||
export * from './features/reconnect/action-definitions'; | ||
export * from './features/reconnect/model'; | ||
@@ -61,3 +58,3 @@ export * from './features/request-response/glsp-action-dispatcher'; | ||
export * from './features/tool-feedback/creation-tool-feedback'; | ||
export * from './features/tool-feedback/cursor-feedback'; | ||
export * from './features/tool-feedback/css-feedback'; | ||
export * from './features/tool-feedback/edge-edit-tool-feedback'; | ||
@@ -64,0 +61,0 @@ export * from './features/tool-feedback/feedback-action-dispatcher'; |
@@ -55,7 +55,5 @@ "use strict"; | ||
__export(require("./features/change-bounds/movement-restrictor")); | ||
__export(require("./features/change-bounds/snap")); | ||
__export(require("./features/context-actions/action-definitions")); | ||
__export(require("./features/command-palette/server-command-palette-provider")); | ||
__export(require("./features/context-menu/mouse-listener")); | ||
__export(require("./features/context-menu/context-menu-service")); | ||
__export(require("./features/context-menu/menu-providers")); | ||
__export(require("./features/edit-label-validation/edit-label-validator")); | ||
@@ -72,3 +70,2 @@ __export(require("./features/execute/execute-command")); | ||
__export(require("./features/rank/model")); | ||
__export(require("./features/reconnect/action-definitions")); | ||
__export(require("./features/reconnect/model")); | ||
@@ -80,3 +77,3 @@ __export(require("./features/request-response/glsp-action-dispatcher")); | ||
__export(require("./features/tool-feedback/creation-tool-feedback")); | ||
__export(require("./features/tool-feedback/cursor-feedback")); | ||
__export(require("./features/tool-feedback/css-feedback")); | ||
__export(require("./features/tool-feedback/edge-edit-tool-feedback")); | ||
@@ -83,0 +80,0 @@ __export(require("./features/tool-feedback/feedback-action-dispatcher")); |
@@ -101,3 +101,3 @@ "use strict"; | ||
registry.register(set_operations_1.OperationKind.RECONNECT_CONNECTION, diagramServer); | ||
registry.register(set_operations_1.OperationKind.REROUTE_CONNECTION, diagramServer); | ||
registry.register(set_operations_1.OperationKind.CHANGE_ROUTING_POINTS, diagramServer); | ||
registry.register(set_operations_1.OperationKind.CREATE_NODE, diagramServer); | ||
@@ -104,0 +104,0 @@ registry.register(set_operations_1.OperationKind.CHANGE_BOUNDS, diagramServer); |
@@ -17,2 +17,3 @@ /******************************************************************************** | ||
import { BoundsAware, Selectable, SModelElement, SRoutableElement, SRoutingHandle } from "sprotty/lib"; | ||
import { ElementAndRoutingPoints } from "src/features/operation/operation-actions"; | ||
export declare function getIndex(element: SModelElement): import("sprotty").SModelIndex<SModelElement>; | ||
@@ -23,6 +24,6 @@ export declare function forEachElement<T>(element: SModelElement, predicate: (element: SModelElement) => element is SModelElement & T, runnable: (element: SModelElement & T) => void): void; | ||
export declare function getSelectedElementCount(element: SModelElement): number; | ||
export declare function isSelected(element: SModelElement | undefined): element is SModelElement & Selectable; | ||
export declare function isNotUndefined<T>(element: T | undefined): element is T; | ||
export declare function addCssClasses(root: SModelElement, cssClasses: string[]): void; | ||
export declare function removeCssClasses(root: SModelElement, cssClasses: string[]): void; | ||
export declare function isNonRoutableSelectedMovableBoundsAware(element: SModelElement): element is SelectableBoundsAware; | ||
export declare function isNonRoutableSelectedBoundsAware(element: SModelElement): element is SelectableBoundsAware; | ||
@@ -43,2 +44,3 @@ export declare function isRoutable<T extends SModelElement>(element: T): element is T & SRoutableElement; | ||
}; | ||
export declare function toElementAndRoutingPoints(element: SRoutableElement): ElementAndRoutingPoints; | ||
//# sourceMappingURL=smodel-util.d.ts.map |
@@ -53,3 +53,3 @@ "use strict"; | ||
getIndex(element).all() | ||
.filter(isSelected) | ||
.filter(lib_1.isSelected) | ||
.forEach(function (e) { return selected = selected + 1; }); | ||
@@ -59,6 +59,2 @@ return selected; | ||
exports.getSelectedElementCount = getSelectedElementCount; | ||
function isSelected(element) { | ||
return isNotUndefined(element) && lib_1.isSelectable(element) && element.selected; | ||
} | ||
exports.isSelected = isSelected; | ||
function isNotUndefined(element) { | ||
@@ -113,4 +109,8 @@ return element !== undefined; | ||
exports.removeCssClasses = removeCssClasses; | ||
function isNonRoutableSelectedMovableBoundsAware(element) { | ||
return isNonRoutableSelectedBoundsAware(element) && lib_1.isMoveable(element); | ||
} | ||
exports.isNonRoutableSelectedMovableBoundsAware = isNonRoutableSelectedMovableBoundsAware; | ||
function isNonRoutableSelectedBoundsAware(element) { | ||
return lib_1.isBoundsAware(element) && isSelected(element) && !isRoutable(element); | ||
return lib_1.isBoundsAware(element) && lib_1.isSelected(element) && !isRoutable(element); | ||
} | ||
@@ -140,2 +140,9 @@ exports.isNonRoutableSelectedBoundsAware = isNonRoutableSelectedBoundsAware; | ||
exports.toElementAndBounds = toElementAndBounds; | ||
function toElementAndRoutingPoints(element) { | ||
return { | ||
elementId: element.id, | ||
newRoutingPoints: element.routingPoints | ||
}; | ||
} | ||
exports.toElementAndRoutingPoints = toElementAndRoutingPoints; | ||
//# sourceMappingURL=smodel-util.js.map |
{ | ||
"name": "@eclipse-glsp/client", | ||
"version": "0.8.0-next.55671b3", | ||
"version": "0.8.0-next.7052c9c", | ||
"description": "A sprotty-based client for GLSP", | ||
@@ -45,2 +45,3 @@ "license": "(EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0)", | ||
"rimraf": "^2.6.1", | ||
"semver": "6.3.0", | ||
"ts-node": "^8.3.0", | ||
@@ -50,5 +51,2 @@ "tslint": "^5.5.0", | ||
}, | ||
"resolutions": { | ||
"**/sprotty": "0.8.0-next.1d772ad" | ||
}, | ||
"scripts": { | ||
@@ -55,0 +53,0 @@ "prepare": "yarn run clean && yarn run build", |
@@ -1,3 +0,9 @@ | ||
# Eclipse GLSP - Client | ||
# Eclipse GLSP - Client [![build-status](https://img.shields.io/jenkins/build?jobUrl=https%3A%2F%2Fci.eclipse.org%2Fglsp%2Fjob%2Feclipse-glsp%2Fjob%2Fglsp-client%2Fjob%2Fmaster%2F)](https://ci.eclipse.org/glsp/job/eclipse-glsp/job/glsp-client/job/master) ![build-status-server](https://img.shields.io/jenkins/build?jobUrl=https://ci.eclipse.org/glsp/job/deploy-npm-glsp-client/&label=publish) | ||
This package contains a client for the [Graphical Language Server Protocol (GLSP)](https://github.com/eclipse-glsp/glsp) based on [Eclipse Sprotty](https://github.com/eclipse/sprotty). | ||
A web-based diagram client framework for the [Graphical Language Server Protocol (GLSP)](https://github.com/eclipse-glsp/glsp) based on [Eclipse Sprotty](https://github.com/eclipse/sprotty). | ||
This project is built with `yarn` and is available from npm via [@eclipse-glsp/client](https://www.npmjs.com/package/@eclipse-glsp/client). | ||
For more information on building and running the web-based GLSP diagram client, see the [GLSP umbrella project](https://github.com/eclipse-glsp/glsp) and please visit the [Eclipse GLSP Website](https://www.eclipse.org/glsp). | ||
![alt](https://www.eclipse.org/glsp/images/diagramanimated.gif) |
@@ -32,4 +32,4 @@ /******************************************************************************** | ||
import { UpdateModelAction, UpdateModelCommand } from "sprotty/lib/features/update/update-model"; | ||
import { IFeedbackActionDispatcher } from "src/features/tool-feedback/feedback-action-dispatcher"; | ||
import { IFeedbackActionDispatcher } from "../../features/tool-feedback/feedback-action-dispatcher"; | ||
import { FeedbackCommand } from "../../features/tool-feedback/model"; | ||
@@ -64,12 +64,14 @@ import { GLSP_TYPES } from "../../types"; | ||
export class FeedbackAwareUpdateModelCommand extends UpdateModelCommand { | ||
protected actionHandlerRegistry?: ActionHandlerRegistry; | ||
constructor(@inject(TYPES.Action) action: UpdateModelAction, | ||
@inject(TYPES.ILogger) protected logger: ILogger, | ||
@inject(GLSP_TYPES.IFeedbackActionDispatcher) @optional() protected readonly feedbackActionDispatcher: IFeedbackActionDispatcher, | ||
@inject(TYPES.ActionHandlerRegistryProvider) protected actionHandlerRegistryProvider: () => Promise<ActionHandlerRegistry>, | ||
@inject(TYPES.ActionHandlerRegistryProvider) actionHandlerRegistryProvider: () => Promise<ActionHandlerRegistry>, | ||
@multiInject(GLSP_TYPES.SModelRootListener) @optional() protected modelRootListeners: SModelRootListener[] = []) { | ||
super(action); | ||
actionHandlerRegistryProvider().then(registry => this.actionHandlerRegistry = registry); | ||
} | ||
protected performUpdate(oldRoot: SModelRoot, newRoot: SModelRoot, context: CommandExecutionContext): CommandReturn { | ||
if (this.feedbackActionDispatcher) { | ||
if (this.feedbackActionDispatcher && this.actionHandlerRegistry) { | ||
// Create a temporary context wich defines the `newRoot` as `root` | ||
@@ -85,10 +87,9 @@ // This way we do not corrupt the redo/undo behavior of the super class | ||
}; | ||
this.actionHandlerRegistryProvider().then(registry => { | ||
const feedbackCommands = this.getFeedbackCommands(registry); | ||
feedbackCommands.forEach(command => command.execute(tempContext)); | ||
}); | ||
const feedbackCommands = this.getFeedbackCommands(this.actionHandlerRegistry); | ||
feedbackCommands.forEach(command => command.execute(tempContext)); | ||
} | ||
this.modelRootListeners.forEach(listener => listener.modelRootChanged(newRoot)); | ||
return super.performUpdate(oldRoot, newRoot, context); | ||
} | ||
@@ -95,0 +96,0 @@ |
@@ -17,274 +17,70 @@ /******************************************************************************** | ||
import { injectable } from "inversify"; | ||
import { | ||
Action, | ||
Bounds, | ||
BoundsAware, | ||
ElementMove, | ||
includes, | ||
isBoundsAware, | ||
isMoveable, | ||
isSelectable, | ||
MoveAction, | ||
Point, | ||
PointToPointLine, | ||
SModelElement | ||
} from "sprotty/lib"; | ||
import { FluentIterable, toArray } from "sprotty/lib/utils/iterable"; | ||
import { Action, BoundsAware, Point, SModelElement, SNode, SParentElement } from "sprotty/lib"; | ||
import { ApplyCursorCSSFeedbackAction, CursorCSS } from "../tool-feedback/cursor-feedback"; | ||
import { isBoundsAwareMoveable } from "./model"; | ||
import { toAbsoluteBounds } from "../../utils/viewpoint-util"; | ||
import { ModifyCSSFeedbackAction } from "../tool-feedback/css-feedback"; | ||
import { isBoundsAwareMoveable, SResizeHandle } from "./model"; | ||
export interface IMovementRestrictor { | ||
attemptMove(element: SModelElement, mousePoint: Point, target: SModelElement, delta: Point, result: Action[]): boolean | ||
validate(newLocation: Point, element: SModelElement): boolean; | ||
cssClasses?: string[]; | ||
} | ||
export function createMovementRestrictionFeedback(element: SModelElement, movementRestrictor: IMovementRestrictor): Action[] { | ||
const result: Action[] = []; | ||
result.push(new ModifyCSSFeedbackAction(element, movementRestrictor.cssClasses)); | ||
if (element instanceof SParentElement) { | ||
element.children.filter(child => child instanceof SResizeHandle).forEach(child => result.push(new ModifyCSSFeedbackAction(child, movementRestrictor.cssClasses))); | ||
} | ||
return result; | ||
} | ||
export function removeMovementRestrictionFeedback(element: SModelElement, movementRestrictor: IMovementRestrictor): Action[] { | ||
const result: Action[] = []; | ||
result.push(new ModifyCSSFeedbackAction(element, undefined, movementRestrictor.cssClasses)); | ||
if (element instanceof SParentElement) { | ||
element.children.filter(child => child instanceof SResizeHandle). | ||
forEach(child => result.push(new ModifyCSSFeedbackAction(child, undefined, movementRestrictor.cssClasses))); | ||
} | ||
return result; | ||
} | ||
@injectable() | ||
export class NoCollisionMovementRestrictor { | ||
hasCollided = false; | ||
/* | ||
* Attempt to perform an element move. Returns true if the move is not restricted anc can be applied successfull and false otherwise | ||
*/ | ||
attemptMove(element: SModelElement, mousePoint: Point, target: SModelElement, delta: Point, result: Action[]): boolean { | ||
export class NoOverlapMovmentRestrictor implements IMovementRestrictor { | ||
validate(newLocation: Point, element: SModelElement): boolean { | ||
if (!isBoundsAwareMoveable(element)) { | ||
return false; | ||
} | ||
let mouseOverElement: boolean = false; | ||
let willOverlap: boolean = false; | ||
// Create ghost element to check possible bounds | ||
// Create ghost element at the newLocation; | ||
const ghostElement = Object.create(element); | ||
ghostElement.bounds = this.getCenteredBoundsToPointer(mousePoint, element.bounds); | ||
// Set type to Ghost to keep tracking it through elements | ||
ghostElement.bounds.x = newLocation.x - element.bounds.width / 2; | ||
ghostElement.bounds.y = newLocation.y - element.bounds.height / 2; | ||
ghostElement.type = "Ghost"; | ||
ghostElement.id = element.id; | ||
// Check collision for gost element (to see when it has passed beyond obstacle) | ||
const collisionTargetsGhost: SModelElement[] = this.getCollisionChain(target, ghostElement, delta, []) | ||
.filter(collidingElement => isSelectable(collidingElement) && !collidingElement.selected); | ||
return !Array.from(element.root.index.all().filter(e => e.id !== ghostElement.id && e !== ghostElement.root && (e instanceof SNode)) | ||
.map(e => e as SModelElement & BoundsAware)).some(e => areOverlapping(e, ghostElement)); | ||
} | ||
// After collision the mouse is back inside the element => change cursor back to default | ||
if (this.hasCollided && includes(element.bounds, mousePoint)) { | ||
mouseOverElement = true; | ||
result.push(new ApplyCursorCSSFeedbackAction(CursorCSS.DEFAULT)); | ||
} | ||
cssClasses = ["movement-not-allowed"]; | ||
} | ||
const selectedElements: FluentIterable<SModelElement> = target.root.index.all() | ||
.filter(selected => isSelectable(selected) && selected.selected); | ||
export function areOverlapping(element1: SModelElement & BoundsAware, element2: SModelElement & BoundsAware) { | ||
const b1 = toAbsoluteBounds(element1); | ||
const b2 = toAbsoluteBounds(element2); | ||
const r1TopLeft: Point = b1; | ||
const r1BottomRight = { x: b1.x + b1.width, y: b1.y + b1.height }; | ||
const r2TopLeft: Point = b2; | ||
const r2BottomRight = { x: b2.x + b2.width, y: b2.y + b2.height }; | ||
// If the ghost element has moved beyond the obstacle move the actual element there aswell | ||
// But only if a single element is selected (multi-selection jumps are not supported) | ||
if (this.hasCollided && collisionTargetsGhost.length === 0 && toArray(selectedElements).length === 1) { | ||
mouseOverElement = true; | ||
result.push(new ApplyCursorCSSFeedbackAction(CursorCSS.DEFAULT)); | ||
// If one rectangle is on left side of other | ||
if (r1TopLeft.x > r2BottomRight.x || r2TopLeft.x > r1BottomRight.x) | ||
return false; | ||
if (element.id === ghostElement.id) { | ||
element.bounds = ghostElement.bounds; | ||
} | ||
} | ||
// Get only the valid, non-slected collision targets to avoid in-selection collisions | ||
const collisionTargets: SModelElement[] = this.getCollisionChain(target, element, delta, []) | ||
.filter(collidingElement => isSelectable(collidingElement) && !collidingElement.selected); | ||
if (collisionTargets.length > 0) { | ||
collisionTargets.forEach(collisionTarget => { | ||
if (isBoundsAware(collisionTarget)) { | ||
// Only snap on first collision to avoid erratic jumps | ||
if (!this.hasCollided) { | ||
const snappedBounds = this.getSnappedBounds(element, collisionTarget); | ||
const snapMoves: ElementMove[] = []; | ||
snapMoves.push({ | ||
elementId: element.id, | ||
fromPosition: { | ||
x: element.position.x, | ||
y: element.position.y | ||
}, | ||
toPosition: { | ||
x: snappedBounds.x, | ||
y: snappedBounds.y | ||
} | ||
}); | ||
result.push(new MoveAction(snapMoves, false)); | ||
} | ||
willOverlap = true; | ||
this.hasCollided = true; | ||
result.push(new ApplyCursorCSSFeedbackAction(CursorCSS.OVERLAP_FORBIDDEN)); | ||
} | ||
}); | ||
} | ||
if ((!willOverlap && !this.hasCollided) || | ||
(this.hasCollided && !willOverlap && mouseOverElement)) { | ||
this.hasCollided = false; | ||
return true; | ||
} | ||
// If one rectangle is above other | ||
if (r1BottomRight.y < r2TopLeft.y || r2BottomRight.y < r1TopLeft.y) | ||
return false; | ||
} | ||
/** | ||
* Used to return the collision target(s) or the collision chain in case of multiple selected elements | ||
*/ | ||
getCollisionChain(target: SModelElement, element: SModelElement, delta: Point, collisionChain: SModelElement[]): SModelElement[] { | ||
if (isBoundsAwareMoveable(element)) { | ||
target.root.index.all() | ||
.filter(candidate => isSelectable(candidate) && element.id !== candidate.id && collisionChain.indexOf(candidate) < 0) | ||
.forEach(candidate => { | ||
if (isMoveable(element) && isMoveable(candidate)) { | ||
if (isBoundsAware(element) && isBoundsAware(candidate)) { | ||
const futureBounds: Bounds = { | ||
x: element.position.x + delta.x, | ||
y: element.position.y + delta.y, | ||
width: element.bounds.width, | ||
height: element.bounds.height | ||
}; | ||
if (isOverlappingBounds(futureBounds, candidate.bounds) && (!isOverlappingBounds(element.bounds, candidate.bounds) || element.type === "Ghost")) { | ||
collisionChain.push(candidate); | ||
if (isSelectable(candidate) && candidate.selected) { | ||
// Check what the selected candidate will collide with and add it to the chain | ||
collisionChain.push.apply(collisionChain, this.getCollisionChain(target, candidate, delta, collisionChain)); | ||
} | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
return collisionChain; | ||
} | ||
return true; | ||
/** | ||
* Returns bounds centered around the point | ||
*/ | ||
getCenteredBoundsToPointer(mousePoint: Point, bounds: Bounds): Bounds { | ||
const middleX = mousePoint.x - bounds.width / 2; | ||
const middleY = mousePoint.y - bounds.height / 2; | ||
const shiftedBounds: Bounds = { x: middleX, y: middleY, width: bounds.width, height: bounds.height }; | ||
return shiftedBounds; | ||
} | ||
// Remove this and use the one from the improved routing branch | ||
getDistanceBetweenParallelLines(p1: Point, p2: Point, secondLine: PointToPointLine): Number { | ||
const numerator: number = Math.abs((secondLine.a * p1.x) + (secondLine.b * p1.y) - secondLine.c); | ||
const denominator: number = Math.sqrt(Math.pow(secondLine.a, 2) + Math.pow(secondLine.b, 2)); | ||
return numerator / denominator; | ||
} | ||
/** | ||
* Snaps the element to the target in case of a collision | ||
*/ | ||
getSnappedBounds(element: SModelElement & BoundsAware, target: SModelElement & BoundsAware): Bounds { | ||
let snappedBounds = element.bounds; | ||
// Build corner points | ||
const elementTopLeft = { | ||
x: element.bounds.x, | ||
y: element.bounds.y | ||
}; | ||
const elementTopRight = { | ||
x: element.bounds.x + element.bounds.width, | ||
y: element.bounds.y | ||
}; | ||
const elementBottomLeft = { | ||
x: element.bounds.x, | ||
y: element.bounds.y + element.bounds.height | ||
}; | ||
const elementBottomRight = { | ||
x: element.bounds.x + element.bounds.width, | ||
y: element.bounds.y + element.bounds.height | ||
}; | ||
const targetTopLeft = { | ||
x: target.bounds.x, | ||
y: target.bounds.y | ||
}; | ||
const targetTopRight = { | ||
x: target.bounds.x + target.bounds.width, | ||
y: target.bounds.y | ||
}; | ||
const targetBottomLeft = { | ||
x: target.bounds.x, | ||
y: target.bounds.y + target.bounds.height | ||
}; | ||
const targetBottomRight = { | ||
x: target.bounds.x + target.bounds.width, | ||
y: target.bounds.y + target.bounds.height | ||
}; | ||
// Build lines | ||
const targetTopLine = new PointToPointLine(targetTopLeft, targetTopRight); | ||
const targetBottomLine = new PointToPointLine(targetBottomLeft, targetBottomRight); | ||
const targetLeftLine = new PointToPointLine(targetTopLeft, targetBottomLeft); | ||
const targetRightLine = new PointToPointLine(targetTopRight, targetBottomRight); | ||
// Compute distances | ||
const distanceTop = this.getDistanceBetweenParallelLines(elementBottomLeft, elementBottomRight, targetTopLine); | ||
const distanceBottom = this.getDistanceBetweenParallelLines(elementTopLeft, elementTopRight, targetBottomLine); | ||
const distanceLeft = this.getDistanceBetweenParallelLines(elementTopLeft, elementBottomLeft, targetRightLine); | ||
const distanceRight = this.getDistanceBetweenParallelLines(elementTopRight, elementBottomRight, targetLeftLine); | ||
const minimumCandidates: number[] = []; | ||
// Overlap on the horizontal lines | ||
if (isOverlapping1Dimension(element.bounds.x, element.bounds.width, target.bounds.x, target.bounds.width)) { | ||
minimumCandidates.push(distanceTop.valueOf()); | ||
minimumCandidates.push(distanceBottom.valueOf()); | ||
} | ||
// Overlap on the horizontal lines | ||
if (isOverlapping1Dimension(element.bounds.y, element.bounds.height, target.bounds.y, target.bounds.height)) { | ||
minimumCandidates.push(distanceLeft.valueOf()); | ||
minimumCandidates.push(distanceRight.valueOf()); | ||
} | ||
// Get minimum distance and then snap accordingly | ||
minimumCandidates.sort((a, b) => a - b); | ||
const minimumDistance = minimumCandidates[0]; | ||
if (minimumDistance === distanceTop) { | ||
snappedBounds = { | ||
x: element.bounds.x, | ||
y: target.bounds.y - 1 - element.bounds.height, | ||
width: element.bounds.width, | ||
height: element.bounds.height | ||
}; | ||
} | ||
if (minimumDistance === distanceBottom) { | ||
snappedBounds = { | ||
x: element.bounds.x, | ||
y: target.bounds.y + target.bounds.height + 1, | ||
width: element.bounds.width, | ||
height: element.bounds.height | ||
}; | ||
} | ||
if (minimumDistance === distanceLeft) { | ||
snappedBounds = { | ||
x: target.bounds.x + target.bounds.width + 1, | ||
y: element.bounds.y, | ||
width: element.bounds.width, | ||
height: element.bounds.height | ||
}; | ||
} | ||
if (minimumDistance === distanceRight) { | ||
snappedBounds = { | ||
x: target.bounds.x - 1 - element.bounds.width, | ||
y: element.bounds.y, | ||
width: element.bounds.width, | ||
height: element.bounds.height | ||
}; | ||
} | ||
return snappedBounds; | ||
} | ||
} | ||
/** | ||
* Used to check if 1D boxes (lines) overlap | ||
*/ | ||
export function isOverlapping1Dimension(x1: number, width1: number, x2: number, width2: number): boolean { | ||
return x1 + width1 >= x2 && x2 + width2 >= x1; | ||
} | ||
/** | ||
* Used to check if 2 bounds are overlapping | ||
*/ | ||
export function isOverlappingBounds(bounds1: Bounds, bounds2: Bounds): boolean { | ||
return isOverlapping1Dimension(bounds1.x, bounds1.width, bounds2.x, bounds2.width) && | ||
isOverlapping1Dimension(bounds1.y, bounds1.height, bounds2.y, bounds2.height); | ||
} | ||
@@ -17,5 +17,4 @@ /******************************************************************************** | ||
import { inject, injectable } from "inversify"; | ||
import { Action, ICommandPaletteActionProvider, LabeledAction, Point, SModelElement, TYPES } from "sprotty/lib"; | ||
import { Action, ICommandPaletteActionProvider, isSelected, LabeledAction, Point, SModelElement, TYPES } from "sprotty/lib"; | ||
import { isSelected } from "../../utils/smodel-util"; | ||
import { ContextActions, isSetContextActionsAction, RequestContextActions } from "../context-actions/action-definitions"; | ||
@@ -22,0 +21,0 @@ import { GLSPActionDispatcher } from "../request-response/glsp-action-dispatcher"; |
@@ -19,25 +19,8 @@ /******************************************************************************** | ||
import { GLSP_TYPES } from "../../types"; | ||
import { IContextMenuService } from "./context-menu-service"; | ||
import { ContextMenuProviderRegistry } from "./menu-providers"; | ||
import { ContextMenuMouseListener } from "./mouse-listener"; | ||
import { ServerContextMenuItemProvider } from "./server-context-menu-provider"; | ||
const glspContextMenuModule = new ContainerModule(bind => { | ||
bind(GLSP_TYPES.IContextMenuServiceProvider).toProvider<IContextMenuService>(ctx => { | ||
return () => { | ||
return new Promise<IContextMenuService>((resolve, reject) => { | ||
if (ctx.container.isBound(GLSP_TYPES.IContextMenuService)) { | ||
resolve(ctx.container.get<IContextMenuService>(GLSP_TYPES.IContextMenuService)); | ||
} else { | ||
reject(); | ||
} | ||
}); | ||
}; | ||
}); | ||
bind(TYPES.MouseListener).to(ContextMenuMouseListener); | ||
bind(GLSP_TYPES.IContextMenuProviderRegistry).to(ContextMenuProviderRegistry); | ||
bind(GLSP_TYPES.IContextMenuProvider).to(ServerContextMenuItemProvider); | ||
bind(TYPES.IContextMenuItemProvider).to(ServerContextMenuItemProvider); | ||
}); | ||
export default glspContextMenuModule; |
@@ -17,8 +17,15 @@ /******************************************************************************** | ||
import { inject, injectable } from "inversify"; | ||
import { Action, LabeledAction, Point, SModelElement, subtract, TYPES } from "sprotty/lib"; | ||
import { | ||
Action, | ||
IContextMenuItemProvider, | ||
isSelected, | ||
LabeledAction, | ||
Point, | ||
SModelElement, | ||
subtract, | ||
TYPES | ||
} from "sprotty/lib"; | ||
import { isSelected } from "../../utils/smodel-util"; | ||
import { ContextActions, isSetContextActionsAction, RequestContextActions } from "../context-actions/action-definitions"; | ||
import { GLSPActionDispatcher } from "../request-response/glsp-action-dispatcher"; | ||
import { IContextMenuItemProvider } from "./menu-providers"; | ||
@@ -25,0 +32,0 @@ export namespace ServerContextMenu { |
@@ -56,2 +56,15 @@ /******************************************************************************** | ||
export class ReconnectConnectionOperationAction implements Action { | ||
readonly kind = OperationKind.RECONNECT_CONNECTION; | ||
constructor(public readonly connectionElementId: string, | ||
public readonly sourceElementId: string, | ||
public readonly targetElementId: string) { } | ||
} | ||
export class ChangeRoutingPointsOperation implements Action { | ||
readonly kind = OperationKind.CHANGE_ROUTING_POINTS; | ||
constructor(public newRoutingPoints: ElementAndRoutingPoints[]) { } | ||
} | ||
export class GenericOperationAction implements Action { | ||
@@ -65,1 +78,5 @@ readonly kind = OperationKind.GENERIC; | ||
export interface ElementAndRoutingPoints { | ||
elementId: string | ||
newRoutingPoints?: Point[]; | ||
} |
@@ -22,5 +22,5 @@ /******************************************************************************** | ||
export const RECONNECT_CONNECTION = "reconnectConnection"; | ||
export const REROUTE_CONNECTION = "rerouteConnection"; | ||
export const DELETE_ELEMENT = "delete"; | ||
export const CHANGE_BOUNDS = "changeBoundsOperation"; | ||
export const CHANGE_ROUTING_POINTS = "changeRoutingPoints"; | ||
export const DELETE_ELEMENT = "deleteElement"; | ||
export const CHANGE_BOUNDS = "changeBounds"; | ||
export const CHANGE_CONTAINER = "changeContainer"; | ||
@@ -27,0 +27,0 @@ export const GENERIC = "generic"; |
@@ -21,3 +21,2 @@ /******************************************************************************** | ||
const requestResponseModule = new ContainerModule((bind, unbind, isBound, rebind) => { | ||
@@ -24,0 +23,0 @@ rebind(TYPES.IActionDispatcher).to(GLSPActionDispatcher).inSingletonScope(); |
@@ -36,5 +36,5 @@ /******************************************************************************** | ||
import { isNotUndefined } from "../../utils/smodel-util"; | ||
import { getAbsolutePosition } from "../../utils/viewpoint-util"; | ||
import { addResizeHandles, isBoundsAwareMoveable, isResizable, removeResizeHandles } from "../change-bounds/model"; | ||
import { IMovementRestrictor } from "../change-bounds/movement-restrictor"; | ||
import { addResizeHandles, isResizable, removeResizeHandles, SResizeHandle } from "../change-bounds/model"; | ||
import { createMovementRestrictionFeedback, removeMovementRestrictionFeedback } from "../change-bounds/movement-restrictor"; | ||
import { ChangeBoundsTool } from "../tools/change-bounds-tool"; | ||
import { FeedbackCommand } from "./model"; | ||
@@ -54,5 +54,8 @@ | ||
export class ShowChangeBoundsToolResizeFeedbackCommand extends FeedbackCommand { | ||
static readonly KIND = 'showChangeBoundsToolResizeFeedback'; | ||
static readonly KIND = "showChangeBoundsToolResizeFeedback"; | ||
constructor(@inject(TYPES.Action) protected action: ShowChangeBoundsToolResizeFeedbackAction) { | ||
constructor( | ||
@inject(TYPES.Action) | ||
protected action: ShowChangeBoundsToolResizeFeedbackAction | ||
) { | ||
super(); | ||
@@ -63,3 +66,6 @@ } | ||
const index = context.root.index; | ||
index.all().filter(isResizable).forEach(removeResizeHandles); | ||
index | ||
.all() | ||
.filter(isResizable) | ||
.forEach(removeResizeHandles); | ||
@@ -78,5 +84,8 @@ if (isNotUndefined(this.action.elementId)) { | ||
export class HideChangeBoundsToolResizeFeedbackCommand extends FeedbackCommand { | ||
static readonly KIND = 'hideChangeBoundsToolResizeFeedback'; | ||
static readonly KIND = "hideChangeBoundsToolResizeFeedback"; | ||
constructor(@inject(TYPES.Action) protected action: HideChangeBoundsToolResizeFeedbackAction) { | ||
constructor( | ||
@inject(TYPES.Action) | ||
protected action: HideChangeBoundsToolResizeFeedbackAction | ||
) { | ||
super(); | ||
@@ -87,3 +96,6 @@ } | ||
const index = context.root.index; | ||
index.all().filter(isResizable).forEach(removeResizeHandles); | ||
index | ||
.all() | ||
.filter(isResizable) | ||
.forEach(removeResizeHandles); | ||
return context.root; | ||
@@ -102,11 +114,15 @@ } | ||
hasDragged = false; | ||
lastDragPosition: Point | undefined; | ||
constructor(protected movementRestrictor?: IMovementRestrictor) { super(); } | ||
startDragPosition: Point | undefined; | ||
elementId2startPos = new Map<string, Point>(); | ||
constructor(protected tool: ChangeBoundsTool) { | ||
super(); | ||
} | ||
mouseDown(target: SModelElement, event: MouseEvent): Action[] { | ||
if (event.button === 0) { | ||
if (event.button === 0 && !(target instanceof SResizeHandle)) { | ||
const moveable = findParentByFeature(target, isMoveable); | ||
if (moveable !== undefined) { | ||
this.lastDragPosition = { x: event.pageX, y: event.pageY }; | ||
this.startDragPosition = { x: event.pageX, y: event.pageY }; | ||
} else { | ||
this.lastDragPosition = undefined; | ||
this.startDragPosition = undefined; | ||
} | ||
@@ -120,44 +136,87 @@ this.hasDragged = false; | ||
const result: Action[] = []; | ||
if (event.buttons === 0) | ||
this.mouseUp(target, event); | ||
else if (this.lastDragPosition) { | ||
const viewport = findParentByFeature(target, isViewport); | ||
if (event.buttons === 0) this.mouseUp(target, event); | ||
else if (this.startDragPosition) { | ||
if (this.elementId2startPos.size === 0) { | ||
this.collectStartPositions(target.root); | ||
} | ||
this.hasDragged = true; | ||
const zoom = viewport ? viewport.zoom : 1; | ||
const mousePoint: Point = getAbsolutePosition(target, event); | ||
const dx = (event.pageX - this.lastDragPosition.x) / zoom; | ||
const dy = (event.pageY - this.lastDragPosition.y) / zoom; | ||
const nodeMoves: ElementMove[] = []; | ||
let isValidMove: boolean = true; | ||
const moveAction = this.getElementMoves(target, event, false); | ||
if (moveAction) result.push(moveAction); | ||
} | ||
return result; | ||
} | ||
target.root.index.all() | ||
.filter(element => isSelectable(element) && element.selected) | ||
.forEach(element => { | ||
if (isBoundsAwareMoveable(element)) { | ||
// If a movement restrictor is bound attemt a non restricted move | ||
if (this.movementRestrictor) { | ||
isValidMove = this.movementRestrictor.attemptMove(element, mousePoint, target, { x: dx, y: dy }, result); | ||
} | ||
} | ||
if (isMoveable(element) && isValidMove) { | ||
nodeMoves.push({ | ||
elementId: element.id, | ||
fromPosition: { | ||
x: element.position.x, | ||
y: element.position.y | ||
}, | ||
toPosition: { | ||
x: element.position.x + dx, | ||
y: element.position.y + dy | ||
} | ||
}); | ||
} | ||
}); | ||
this.lastDragPosition = { x: event.pageX, y: event.pageY }; | ||
if (nodeMoves.length > 0 && isValidMove) { | ||
result.push(new MoveAction(nodeMoves, false)); | ||
protected collectStartPositions(root: SModelRoot) { | ||
root.index | ||
.all() | ||
.filter(element => isSelectable(element) && element.selected) | ||
.forEach(element => { | ||
if (isMoveable(element)) { | ||
this.elementId2startPos.set(element.id, element.position); | ||
} | ||
}); | ||
} | ||
protected getElementMoves(target: SModelElement, event: MouseEvent, isFinished: boolean): MoveAction | undefined { | ||
if (!this.startDragPosition) return undefined; | ||
const elementMoves: ElementMove[] = []; | ||
const viewport = findParentByFeature(target, isViewport); | ||
const zoom = viewport ? viewport.zoom : 1; | ||
const delta = { | ||
x: (event.pageX - this.startDragPosition.x) / zoom, | ||
y: (event.pageY - this.startDragPosition.y) / zoom | ||
}; | ||
this.elementId2startPos.forEach((startPosition, elementId) => { | ||
const element = target.root.index.getById(elementId); | ||
if (element) { | ||
let toPosition = this.snap( | ||
{ | ||
x: startPosition.x + delta.x, | ||
y: startPosition.y + delta.y | ||
}, | ||
element, | ||
!event.shiftKey | ||
); | ||
if (isMoveable(element)) { | ||
toPosition = this.validateMove(startPosition, toPosition, element, isFinished); | ||
elementMoves.push({ | ||
elementId: element.id, | ||
fromPosition: { | ||
x: element.position.x, | ||
y: element.position.y | ||
}, | ||
toPosition | ||
}); | ||
} | ||
} | ||
}); | ||
if (elementMoves.length > 0) | ||
return new MoveAction(elementMoves, false, isFinished); | ||
else return undefined; | ||
} | ||
protected validateMove(startPostion: Point, toPosition: Point, element: SModelElement, isFinished: boolean) { | ||
let newPosition = toPosition; | ||
if (this.tool.movementRestrictor) { | ||
const valid = this.tool.movementRestrictor.validate(toPosition, element); | ||
let actions; | ||
if (!valid) { | ||
actions = createMovementRestrictionFeedback(element, this.tool.movementRestrictor); | ||
if (isFinished) { | ||
newPosition = startPostion; | ||
} | ||
} else { | ||
actions = removeMovementRestrictionFeedback(element, this.tool.movementRestrictor); | ||
} | ||
this.tool.dispatchFeedback(this, actions); | ||
} | ||
return result; | ||
return newPosition; | ||
} | ||
protected snap(position: Point, element: SModelElement, isSnap: boolean): Point { | ||
if (isSnap && this.tool.snapper) return this.tool.snapper.snap(position, element); | ||
else return position; | ||
} | ||
@@ -171,5 +230,17 @@ mouseEnter(target: SModelElement, event: MouseEvent): Action[] { | ||
mouseUp(target: SModelElement, event: MouseEvent): Action[] { | ||
const result: Action[] = []; | ||
if (this.startDragPosition) { | ||
const moveAction = this.getElementMoves(target, event, true); | ||
if (moveAction) { | ||
result.push(moveAction); | ||
} | ||
if (this.tool.movementRestrictor) { | ||
result.push(...removeMovementRestrictionFeedback(target, this.tool.movementRestrictor)); | ||
} | ||
} | ||
this.hasDragged = false; | ||
this.lastDragPosition = undefined; | ||
return []; | ||
this.startDragPosition = undefined; | ||
this.elementId2startPos.clear(); | ||
return result; | ||
} | ||
@@ -180,3 +251,2 @@ | ||
} | ||
} |
@@ -145,3 +145,3 @@ /******************************************************************************** | ||
const feedbackEdgeSchema = <SEdgeSchema>{ | ||
type: 'edge', | ||
type: elementTypeId, | ||
id: feedbackEdgeId(root), | ||
@@ -148,0 +148,0 @@ sourceId: source.id, |
@@ -26,3 +26,3 @@ /******************************************************************************** | ||
import { DrawFeedbackEdgeCommand, FeedbackEdgeEnd, RemoveFeedbackEdgeCommand } from "./creation-tool-feedback"; | ||
import { ApplyCursorCSSFeedbackActionCommand } from "./cursor-feedback"; | ||
import { ModifyCssFeedbackCommand } from "./css-feedback"; | ||
import { | ||
@@ -40,4 +40,5 @@ DrawFeedbackEdgeSourceCommand, | ||
configureCommand({ bind, isBound }, ModifyCssFeedbackCommand); | ||
// create node and edge tool feedback | ||
configureCommand({ bind, isBound }, ApplyCursorCSSFeedbackActionCommand); | ||
configureCommand({ bind, isBound }, DrawFeedbackEdgeCommand); | ||
@@ -44,0 +45,0 @@ configureCommand({ bind, isBound }, RemoveFeedbackEdgeCommand); |
@@ -31,2 +31,3 @@ /******************************************************************************** | ||
isConnectable, | ||
isSelected, | ||
isViewport, | ||
@@ -46,3 +47,3 @@ MouseListener, | ||
import { isNotUndefined, isRoutable, isRoutingHandle, isSelected } from "../../utils/smodel-util"; | ||
import { isNotUndefined, isRoutable, isRoutingHandle } from "../../utils/smodel-util"; | ||
import { getAbsolutePosition } from "../../utils/viewpoint-util"; | ||
@@ -49,0 +50,0 @@ import { addReconnectHandles, removeReconnectHandles } from "../reconnect/model"; |
@@ -19,10 +19,16 @@ /******************************************************************************** | ||
Action, | ||
Bounds, | ||
BoundsAware, | ||
Dimension, | ||
EdgeRouterRegistry, | ||
ElementAndBounds, | ||
findParentByFeature, | ||
ISnapper, | ||
isSelected, | ||
isViewport, | ||
KeyTool, | ||
ModelLayoutOptions, | ||
MouseListener, | ||
Point, | ||
SConnectableElement, | ||
SetBoundsAction, | ||
@@ -32,11 +38,25 @@ SModelElement, | ||
SParentElement, | ||
Tool | ||
Tool, | ||
TYPES | ||
} from "sprotty/lib"; | ||
import { GLSP_TYPES } from "../../types"; | ||
import { forEachElement, isNonRoutableSelectedBoundsAware, isSelected, toElementAndBounds } from "../../utils/smodel-util"; | ||
import { | ||
forEachElement, | ||
isNonRoutableSelectedMovableBoundsAware, | ||
toElementAndBounds, | ||
toElementAndRoutingPoints | ||
} from "../../utils/smodel-util"; | ||
import { isBoundsAwareMoveable, isResizable, ResizeHandleLocation, SResizeHandle } from "../change-bounds/model"; | ||
import { IMovementRestrictor } from "../change-bounds/movement-restrictor"; | ||
import { | ||
createMovementRestrictionFeedback, | ||
IMovementRestrictor, | ||
removeMovementRestrictionFeedback | ||
} from "../change-bounds/movement-restrictor"; | ||
import { IMouseTool } from "../mouse-tool/mouse-tool"; | ||
import { ChangeBoundsOperationAction } from "../operation/operation-actions"; | ||
import { | ||
ChangeBoundsOperationAction, | ||
ChangeRoutingPointsOperation, | ||
ElementAndRoutingPoints | ||
} from "../operation/operation-actions"; | ||
import { SelectionListener, SelectionService } from "../select/selection-service"; | ||
@@ -48,3 +68,4 @@ import { | ||
} from "../tool-feedback/change-bounds-tool-feedback"; | ||
import { IFeedbackActionDispatcher } from "../tool-feedback/feedback-action-dispatcher"; | ||
import { IFeedbackActionDispatcher, IFeedbackEmitter } from "../tool-feedback/feedback-action-dispatcher"; | ||
import { DragAwareMouseListener } from "./drag-aware-mouse-listener"; | ||
@@ -69,4 +90,4 @@ /** | ||
protected feedbackMoveMouseListener: FeedbackMoveMouseListener; | ||
protected changeBoundsListener: ChangeBoundsListener; | ||
protected feedbackMoveMouseListener: MouseListener; | ||
protected changeBoundsListener: MouseListener & SelectionListener; | ||
@@ -77,16 +98,28 @@ constructor(@inject(GLSP_TYPES.SelectionService) protected selectionService: SelectionService, | ||
@inject(GLSP_TYPES.IFeedbackActionDispatcher) protected feedbackDispatcher: IFeedbackActionDispatcher, | ||
@inject(GLSP_TYPES.IMovementRestrictor) @optional() protected movementRestrictor?: IMovementRestrictor) { } | ||
@inject(EdgeRouterRegistry) @optional() readonly edgeRouterRegistry?: EdgeRouterRegistry, | ||
@inject(TYPES.ISnapper) @optional() readonly snapper?: ISnapper, | ||
@inject(GLSP_TYPES.IMovementRestrictor) @optional() readonly movementRestrictor?: IMovementRestrictor) { } | ||
enable() { | ||
// install feedback move mouse listener for client-side move updates | ||
this.feedbackMoveMouseListener = new FeedbackMoveMouseListener(this.movementRestrictor); | ||
this.feedbackMoveMouseListener = this.createMoveMouseListener(); | ||
this.mouseTool.register(this.feedbackMoveMouseListener); | ||
// instlal change bounds listener for client-side resize updates and server-side updates | ||
this.changeBoundsListener = new ChangeBoundsListener(this); | ||
// install change bounds listener for client-side resize updates and server-side updates | ||
this.changeBoundsListener = this.createChangeBoundsListener(); | ||
this.mouseTool.register(this.changeBoundsListener); | ||
this.selectionService.register(this.changeBoundsListener); | ||
// register feedback | ||
this.feedbackDispatcher.registerFeedback(this, [new ShowChangeBoundsToolResizeFeedbackAction]); | ||
} | ||
protected createMoveMouseListener(): MouseListener { | ||
return new FeedbackMoveMouseListener(this); | ||
} | ||
protected createChangeBoundsListener(): MouseListener & SelectionListener { | ||
return new ChangeBoundsListener(this); | ||
} | ||
disable() { | ||
@@ -96,18 +129,21 @@ this.mouseTool.deregister(this.changeBoundsListener); | ||
this.mouseTool.deregister(this.feedbackMoveMouseListener); | ||
this.feedbackDispatcher.deregisterFeedback(this, [new HideChangeBoundsToolResizeFeedbackAction]); | ||
this.feedbackDispatcher.deregisterFeedback(this.feedbackMoveMouseListener, []); | ||
this.feedbackDispatcher.deregisterFeedback(this.changeBoundsListener, [new HideChangeBoundsToolResizeFeedbackAction]); | ||
} | ||
dispatchFeedback(actions: Action[]) { | ||
this.feedbackDispatcher.registerFeedback(this, actions); | ||
dispatchFeedback(feedbackEmmmiter: IFeedbackEmitter, actions: Action[]) { | ||
this.feedbackDispatcher.registerFeedback(feedbackEmmmiter, actions); | ||
} | ||
} | ||
class ChangeBoundsListener extends MouseListener implements SelectionListener { | ||
export class ChangeBoundsListener extends DragAwareMouseListener implements SelectionListener { | ||
// members for calculating the correct position change | ||
protected lastDragPosition: Point | undefined = undefined; | ||
protected lastDragPosition?: Point; | ||
protected positionDelta: Point = { x: 0, y: 0 }; | ||
protected initialPositon: Point | undefined = undefined; | ||
protected initialBounds: Bounds | undefined; | ||
// members for resize mode | ||
protected activeResizeElementId: string | undefined = undefined; | ||
protected activeResizeHandle: SResizeHandle | undefined = undefined; | ||
protected activeResizeElementId?: string; | ||
protected activeResizeHandle?: SResizeHandle; | ||
@@ -120,21 +156,22 @@ constructor(protected tool: ChangeBoundsTool) { | ||
super.mouseDown(target, event); | ||
const actions: Action[] = []; | ||
if (event.button === 0) { | ||
// check if we have a resize handle (only single-selection) | ||
if (this.activeResizeElementId && target instanceof SResizeHandle) { | ||
this.activeResizeHandle = target; | ||
} else { | ||
this.setActiveResizeElement(target); | ||
} | ||
if (this.activeResizeElementId) { | ||
this.initPosition(event); | ||
} else { | ||
this.reset(); | ||
} | ||
if (event.button !== 0) { | ||
return []; | ||
} | ||
return actions; | ||
// check if we have a resize handle (only single-selection) | ||
if (this.activeResizeElementId && target instanceof SResizeHandle) { | ||
this.activeResizeHandle = target; | ||
} else { | ||
this.setActiveResizeElement(target); | ||
} | ||
if (this.activeResizeElementId) { | ||
this.initPosition(event); | ||
} else { | ||
this.reset(); | ||
} | ||
return []; | ||
} | ||
mouseMove(target: SModelElement, event: MouseEvent): Action[] { | ||
if (this.updatePosition(target, event)) { | ||
super.mouseMove(target, event); | ||
if (this.updatePosition(target, event) && this.activeResizeHandle) { | ||
// rely on the FeedbackMoveMouseListener to update the element bounds of selected elements | ||
@@ -147,5 +184,4 @@ // consider resize handles ourselves | ||
mouseUp(target: SModelElement, event: MouseEvent): Action[] { | ||
super.mouseUp(target, event); | ||
if (!this.hasPositionDelta()) { | ||
draggingMouseUp(target: SModelElement, event: MouseEvent): Action[] { | ||
if (this.lastDragPosition === undefined) { | ||
this.resetPosition(); | ||
@@ -155,18 +191,28 @@ return []; | ||
// no further bound changing, simply send the latest data to the server using a single change bounds action for all relevant elements | ||
const actions: Action[] = []; | ||
if (this.activeResizeHandle) { | ||
// An action. Resize, not move. | ||
// Resize, not move | ||
const resizeElement = findParentByFeature(this.activeResizeHandle, isResizable); | ||
if (this.isActiveResizeElement(resizeElement)) { | ||
createChangeBoundsAction(resizeElement).forEach(action => actions.push(action)); | ||
this.createChangeBoundsAction(resizeElement).forEach(action => actions.push(action)); | ||
} | ||
} else { | ||
// Bounds... Change Bounds. | ||
// Move | ||
const newBounds: ElementAndBounds[] = []; | ||
forEachElement(target, isNonRoutableSelectedBoundsAware, element => | ||
createElementAndBounds(element).forEach(bounds => newBounds.push(bounds))); | ||
const newRoutingPoints: ElementAndRoutingPoints[] = []; | ||
forEachElement(target, isNonRoutableSelectedMovableBoundsAware, element => { | ||
this.createElementAndBounds(element).forEach(bounds => newBounds.push(bounds)); | ||
// If client routing is enabled -> delegate routingpoints of connected edges to server | ||
if (this.tool.edgeRouterRegistry && element instanceof SConnectableElement) { | ||
element.incomingEdges.map(toElementAndRoutingPoints).forEach(ear => newRoutingPoints.push(ear)); | ||
element.outgoingEdges.map(toElementAndRoutingPoints).forEach(ear => newRoutingPoints.push(ear)); | ||
} | ||
}); | ||
if (newBounds.length > 0) { | ||
actions.push(new ChangeBoundsOperationAction(newBounds)); | ||
} | ||
if (newRoutingPoints.length > 0) { | ||
actions.push(new ChangeRoutingPointsOperation(newRoutingPoints)); | ||
} | ||
} | ||
@@ -179,3 +225,3 @@ this.resetPosition(); | ||
if (this.activeResizeElementId) { | ||
if (selectedElements.indexOf(this.activeResizeElementId) > -1) { | ||
if (selectedElements.includes(this.activeResizeElementId)) { | ||
// our active element is still selected, nothing to do | ||
@@ -202,3 +248,3 @@ return; | ||
this.activeResizeElementId = moveableElement.id; | ||
this.tool.dispatchFeedback([new ShowChangeBoundsToolResizeFeedbackAction(this.activeResizeElementId)]); | ||
this.tool.dispatchFeedback(this, [new ShowChangeBoundsToolResizeFeedbackAction(this.activeResizeElementId)]); | ||
return true; | ||
@@ -209,3 +255,3 @@ } | ||
protected isActiveResizeElement(element: SModelElement | undefined): element is SParentElement & BoundsAware { | ||
protected isActiveResizeElement(element?: SModelElement): element is SParentElement & BoundsAware { | ||
return element !== undefined && element.id === this.activeResizeElementId; | ||
@@ -215,3 +261,9 @@ } | ||
protected initPosition(event: MouseEvent) { | ||
this.initialPositon = { x: event.pageX, y: event.pageY }; | ||
this.lastDragPosition = { x: event.pageX, y: event.pageY }; | ||
if (this.activeResizeHandle) { | ||
const resizeElement = findParentByFeature(this.activeResizeHandle, isResizable); | ||
this.initialBounds = { x: resizeElement!.bounds.x, y: resizeElement!.bounds.y, width: resizeElement!.bounds.width, height: resizeElement!.bounds.height }; | ||
} | ||
} | ||
@@ -234,3 +286,3 @@ | ||
protected reset() { | ||
this.tool.dispatchFeedback([new HideChangeBoundsToolResizeFeedbackAction()]); | ||
this.tool.dispatchFeedback(this, [new HideChangeBoundsToolResizeFeedbackAction()]); | ||
this.resetPosition(); | ||
@@ -242,9 +294,6 @@ } | ||
this.lastDragPosition = undefined; | ||
this.initialPositon = undefined; | ||
this.positionDelta = { x: 0, y: 0 }; | ||
} | ||
protected hasPositionDelta(): boolean { | ||
return this.positionDelta.x !== 0 || this.positionDelta.y !== 0; | ||
} | ||
protected handleElementResize(): Action[] { | ||
@@ -260,3 +309,3 @@ if (!this.activeResizeHandle) { | ||
case ResizeHandleLocation.TopLeft: | ||
createSetBoundsAction(resizeElement, | ||
this.createSetBoundsAction(resizeElement, | ||
resizeElement.bounds.x + this.positionDelta.x, | ||
@@ -269,3 +318,3 @@ resizeElement.bounds.y + this.positionDelta.y, | ||
case ResizeHandleLocation.TopRight: | ||
createSetBoundsAction(resizeElement, | ||
this.createSetBoundsAction(resizeElement, | ||
resizeElement.bounds.x, | ||
@@ -278,3 +327,3 @@ resizeElement.bounds.y + this.positionDelta.y, | ||
case ResizeHandleLocation.BottomLeft: | ||
createSetBoundsAction(resizeElement, | ||
this.createSetBoundsAction(resizeElement, | ||
resizeElement.bounds.x + this.positionDelta.x, | ||
@@ -287,3 +336,3 @@ resizeElement.bounds.y, | ||
case ResizeHandleLocation.BottomRight: | ||
createSetBoundsAction(resizeElement, | ||
this.createSetBoundsAction(resizeElement, | ||
resizeElement.bounds.x, | ||
@@ -299,43 +348,80 @@ resizeElement.bounds.y, | ||
} | ||
} | ||
function createChangeBoundsAction(element: SModelElement & BoundsAware): Action[] { | ||
if (isValidBoundChange(element, element.bounds, element.bounds)) { | ||
return [new ChangeBoundsOperationAction([toElementAndBounds(element)])]; | ||
protected createChangeBoundsAction(element: SModelElement & BoundsAware): Action[] { | ||
if (this.isValidBoundChange(element, element.bounds, element.bounds)) { | ||
return [new ChangeBoundsOperationAction([toElementAndBounds(element)])]; | ||
} else if (this.initialBounds) { | ||
const actions: Action[] = []; | ||
if (this.tool.movementRestrictor) { | ||
actions.push(...removeMovementRestrictionFeedback(element, this.tool.movementRestrictor)); | ||
} | ||
actions.push(new SetBoundsAction([{ elementId: element.id, newPosition: this.initialBounds, newSize: this.initialBounds }])); | ||
return actions; | ||
} | ||
return []; | ||
} | ||
return []; | ||
} | ||
function createElementAndBounds(element: SModelElement & BoundsAware): ElementAndBounds[] { | ||
if (isValidBoundChange(element, element.bounds, element.bounds)) { | ||
return [toElementAndBounds(element)]; | ||
protected createElementAndBounds(element: SModelElement & BoundsAware): ElementAndBounds[] { | ||
if (this.isValidBoundChange(element, element.bounds, element.bounds)) { | ||
return [toElementAndBounds(element)]; | ||
} | ||
return []; | ||
} | ||
return []; | ||
} | ||
function createSetBoundsAction(element: SModelElement & BoundsAware, x: number, y: number, width: number, height: number): Action[] { | ||
const newPosition = { x, y }; | ||
const newSize = { width, height }; | ||
if (isValidBoundChange(element, newPosition, newSize)) { | ||
return [new SetBoundsAction([{ elementId: element.id, newPosition, newSize }])]; | ||
protected createSetBoundsAction(element: SModelElement & BoundsAware, x: number, y: number, width: number, height: number): Action[] { | ||
const newPosition = { x, y }; | ||
const newSize = { width, height }; | ||
const result: Action[] = []; | ||
if (this.isValidBoundChange(element, newPosition, newSize)) { | ||
if (this.tool.movementRestrictor) { | ||
result.push(...removeMovementRestrictionFeedback(element, this.tool.movementRestrictor)); | ||
} | ||
result.push(new SetBoundsAction([{ elementId: element.id, newPosition, newSize }])); | ||
} else if (this.isValidSize(element, newSize)) { | ||
if (this.tool.movementRestrictor) { | ||
result.push(...createMovementRestrictionFeedback(element, this.tool.movementRestrictor)); | ||
} | ||
result.push(new SetBoundsAction([{ elementId: element.id, newPosition, newSize }])); | ||
} | ||
return result; | ||
} | ||
return []; | ||
} | ||
protected isValidBoundChange(element: SModelElement & BoundsAware, newPosition: Point, newSize: Dimension): boolean { | ||
const valid = this.isValidSize(element, newSize); | ||
if (this.tool.movementRestrictor) { | ||
return valid && this.tool.movementRestrictor.validate(newPosition, element); | ||
} | ||
return valid; | ||
} | ||
function isValidBoundChange(element: SModelElement & BoundsAware, newPosition: Point, newSize: Dimension): boolean { | ||
return newSize.width >= minWidth(element) && newSize.height >= minHeight(element); | ||
} | ||
protected isValidSize(element: SModelElement & BoundsAware, size: Dimension) { | ||
return size.width >= this.minWidth(element) && size.height >= this.minHeight(element); | ||
} | ||
function minWidth(element: SModelElement & BoundsAware): number { | ||
// currently there are no element-specific constraints | ||
return 1; | ||
} | ||
protected minWidth(element: SModelElement & BoundsAware): number { | ||
const layoutOptions = this.getLayoutOptions(element); | ||
if (layoutOptions !== undefined && typeof layoutOptions.minWidth === 'number') { | ||
return layoutOptions.minWidth; | ||
} | ||
return 1; | ||
} | ||
function minHeight(element: SModelElement & BoundsAware): number { | ||
// currently there are no element-specific constraints | ||
return 1; | ||
protected minHeight(element: SModelElement & BoundsAware): number { | ||
const layoutOptions = this.getLayoutOptions(element); | ||
if (layoutOptions !== undefined && typeof layoutOptions.minHeight === 'number') { | ||
return layoutOptions.minHeight; | ||
} | ||
return 1; | ||
} | ||
protected getLayoutOptions(element: SModelElement): ModelLayoutOptions | undefined { | ||
const layoutOptions = (element as any).layoutOptions; | ||
if (layoutOptions !== undefined) { | ||
return layoutOptions as ModelLayoutOptions; | ||
} | ||
return undefined; | ||
} | ||
} | ||
@@ -42,3 +42,3 @@ /******************************************************************************** | ||
} from "../tool-feedback/creation-tool-feedback"; | ||
import { ApplyCursorCSSFeedbackAction, CursorCSS } from "../tool-feedback/cursor-feedback"; | ||
import { CursorCSS, cursorFeedbackAction } from "../tool-feedback/css-feedback"; | ||
import { IFeedbackActionDispatcher } from "../tool-feedback/feedback-action-dispatcher"; | ||
@@ -68,3 +68,3 @@ import { DragAwareMouseListener } from "./drag-aware-mouse-listener"; | ||
this.mouseTool.register(this.creationToolMouseListener); | ||
this.feedbackDispatcher.registerFeedback(this, [new ApplyCursorCSSFeedbackAction(CursorCSS.NODE_CREATION)]); | ||
this.feedbackDispatcher.registerFeedback(this, [cursorFeedbackAction(CursorCSS.NODE_CREATION)]); | ||
} | ||
@@ -74,3 +74,3 @@ | ||
this.mouseTool.deregister(this.creationToolMouseListener); | ||
this.feedbackDispatcher.deregisterFeedback(this, [new ApplyCursorCSSFeedbackAction()]); | ||
this.feedbackDispatcher.deregisterFeedback(this, [cursorFeedbackAction()]); | ||
} | ||
@@ -112,4 +112,4 @@ | ||
const feedback = this.creationAllowed(this.elementTypeId) | ||
? new ApplyCursorCSSFeedbackAction(CursorCSS.NODE_CREATION) : | ||
new ApplyCursorCSSFeedbackAction(CursorCSS.OPERATION_NOT_ALLOWED); | ||
? cursorFeedbackAction(CursorCSS.NODE_CREATION) : | ||
cursorFeedbackAction(CursorCSS.OPERATION_NOT_ALLOWED); | ||
this.tool.dispatchFeedback([feedback]); | ||
@@ -145,3 +145,3 @@ } | ||
this.mouseTool.register(this.feedbackEndMovingMouseListener); | ||
this.dispatchFeedback([new ApplyCursorCSSFeedbackAction(CursorCSS.OPERATION_NOT_ALLOWED)]); | ||
this.dispatchFeedback([cursorFeedbackAction(CursorCSS.OPERATION_NOT_ALLOWED)]); | ||
} | ||
@@ -152,3 +152,3 @@ | ||
this.mouseTool.deregister(this.feedbackEndMovingMouseListener); | ||
this.feedbackDispatcher.deregisterFeedback(this, [new RemoveFeedbackEdgeAction(), new ApplyCursorCSSFeedbackAction()]); | ||
this.feedbackDispatcher.deregisterFeedback(this, [new RemoveFeedbackEdgeAction(), cursorFeedbackAction()]); | ||
} | ||
@@ -230,8 +230,8 @@ | ||
if (this.allowedTarget) { | ||
const action = !this.isSourceSelected() ? new ApplyCursorCSSFeedbackAction(CursorCSS.EDGE_CREATION_SOURCE) : | ||
new ApplyCursorCSSFeedbackAction(CursorCSS.EDGE_CREATION_TARGET); | ||
const action = !this.isSourceSelected() ? cursorFeedbackAction(CursorCSS.EDGE_CREATION_SOURCE) : | ||
cursorFeedbackAction(CursorCSS.EDGE_CREATION_TARGET); | ||
return [action]; | ||
} | ||
} | ||
return [new ApplyCursorCSSFeedbackAction(CursorCSS.OPERATION_NOT_ALLOWED)]; | ||
return [cursorFeedbackAction(CursorCSS.OPERATION_NOT_ALLOWED)]; | ||
} | ||
@@ -238,0 +238,0 @@ return []; |
@@ -34,3 +34,3 @@ /******************************************************************************** | ||
import { DeleteElementOperationAction } from "../operation/operation-actions"; | ||
import { ApplyCursorCSSFeedbackAction, CursorCSS } from "../tool-feedback/cursor-feedback"; | ||
import { CursorCSS, cursorFeedbackAction } from "../tool-feedback/css-feedback"; | ||
import { IFeedbackActionDispatcher } from "../tool-feedback/feedback-action-dispatcher"; | ||
@@ -87,3 +87,3 @@ | ||
this.mouseTool.register(this.deleteToolMouseListener); | ||
this.feedbackDispatcher.registerFeedback(this, [new ApplyCursorCSSFeedbackAction(CursorCSS.ELEMENT_DELETION)]); | ||
this.feedbackDispatcher.registerFeedback(this, [cursorFeedbackAction(CursorCSS.ELEMENT_DELETION)]); | ||
} | ||
@@ -93,3 +93,3 @@ | ||
this.mouseTool.deregister(this.deleteToolMouseListener); | ||
this.feedbackDispatcher.registerFeedback(this, [new ApplyCursorCSSFeedbackAction()]); | ||
this.feedbackDispatcher.registerFeedback(this, [cursorFeedbackAction()]); | ||
} | ||
@@ -96,0 +96,0 @@ } |
@@ -28,7 +28,7 @@ /******************************************************************************** | ||
private isMouseDown: boolean = false; | ||
private isMouseDrag: boolean = false; | ||
private _isMouseDown: boolean = false; | ||
private _isMouseDrag: boolean = false; | ||
mouseDown(target: SModelElement, event: MouseEvent): Action[] { | ||
this.isMouseDown = true; | ||
this._isMouseDown = true; | ||
return []; | ||
@@ -38,4 +38,4 @@ } | ||
mouseMove(target: SModelElement, event: MouseEvent): Action[] { | ||
if (this.isMouseDown) { | ||
this.isMouseDrag = true; | ||
if (this._isMouseDown) { | ||
this._isMouseDrag = true; | ||
} | ||
@@ -46,5 +46,5 @@ return []; | ||
mouseUp(element: SModelElement, event: MouseEvent): Action[] { | ||
this.isMouseDown = false; | ||
if (this.isMouseDrag) { | ||
this.isMouseDrag = false; | ||
this._isMouseDown = false; | ||
if (this._isMouseDrag) { | ||
this._isMouseDrag = false; | ||
return this.draggingMouseUp(element, event); | ||
@@ -64,6 +64,10 @@ } | ||
get isDragging() { | ||
return this.isMouseDrag; | ||
get isMouseDrag() { | ||
return this._isMouseDrag; | ||
} | ||
get isMouseDown() { | ||
return this._isMouseDown; | ||
} | ||
} |
@@ -25,2 +25,3 @@ /******************************************************************************** | ||
isConnectable, | ||
isSelected, | ||
MouseListener, | ||
@@ -35,5 +36,5 @@ SModelElement, | ||
import { GLSP_TYPES } from "../../types"; | ||
import { isRoutable, isRoutingHandle, isSelected } from "../../utils/smodel-util"; | ||
import { isRoutable, isRoutingHandle } from "../../utils/smodel-util"; | ||
import { IMouseTool } from "../mouse-tool/mouse-tool"; | ||
import { ReconnectConnectionOperationAction, RerouteConnectionOperationAction } from "../reconnect/action-definitions"; | ||
import { ChangeRoutingPointsOperation, ReconnectConnectionOperationAction } from "../operation/operation-actions"; | ||
import { | ||
@@ -48,3 +49,3 @@ isReconnectable, | ||
import { DrawFeedbackEdgeAction, feedbackEdgeId, RemoveFeedbackEdgeAction } from "../tool-feedback/creation-tool-feedback"; | ||
import { ApplyCursorCSSFeedbackAction, CursorCSS } from "../tool-feedback/cursor-feedback"; | ||
import { CursorCSS, cursorFeedbackAction } from "../tool-feedback/css-feedback"; | ||
import { | ||
@@ -153,3 +154,3 @@ DrawFeedbackEdgeSourceAction, | ||
this.tool.dispatchFeedback([new HideEdgeReconnectHandlesFeedbackAction(), | ||
new ApplyCursorCSSFeedbackAction(CursorCSS.EDGE_RECONNECT), | ||
cursorFeedbackAction(CursorCSS.EDGE_RECONNECT), | ||
new DrawFeedbackEdgeSourceAction(this.edge.type, this.edge.targetId)]); | ||
@@ -159,3 +160,3 @@ this.reconnectMode = "NEW_SOURCE"; | ||
this.tool.dispatchFeedback([new HideEdgeReconnectHandlesFeedbackAction(), | ||
new ApplyCursorCSSFeedbackAction(CursorCSS.EDGE_CREATION_TARGET), | ||
cursorFeedbackAction(CursorCSS.EDGE_CREATION_TARGET), | ||
new DrawFeedbackEdgeAction(this.edge.type, this.edge.sourceId)]); | ||
@@ -246,3 +247,3 @@ this.reconnectMode = "NEW_TARGET"; | ||
if (latestEdge && isRoutable(latestEdge)) { | ||
result.push(new RerouteConnectionOperationAction(latestEdge.id, latestEdge.routingPoints)); | ||
result.push(new ChangeRoutingPointsOperation([{ elementId: latestEdge.id, newRoutingPoints: latestEdge.routingPoints }])); | ||
this.routingHandle = undefined; | ||
@@ -263,7 +264,7 @@ } | ||
this.tool.dispatchFeedback([new ApplyCursorCSSFeedbackAction(CursorCSS.EDGE_RECONNECT)]); | ||
this.tool.dispatchFeedback([cursorFeedbackAction(CursorCSS.EDGE_RECONNECT)]); | ||
return []; | ||
} | ||
} | ||
this.tool.dispatchFeedback([new ApplyCursorCSSFeedbackAction(CursorCSS.OPERATION_NOT_ALLOWED)]); | ||
this.tool.dispatchFeedback([cursorFeedbackAction(CursorCSS.OPERATION_NOT_ALLOWED)]); | ||
} | ||
@@ -322,5 +323,5 @@ } | ||
result.push(...[new HideEdgeReconnectHandlesFeedbackAction(), | ||
new ApplyCursorCSSFeedbackAction(), new RemoveFeedbackEdgeAction()]); | ||
cursorFeedbackAction(), new RemoveFeedbackEdgeAction()]); | ||
this.tool.dispatchFeedback(result); | ||
} | ||
} |
@@ -38,7 +38,5 @@ /******************************************************************************** | ||
export * from './features/change-bounds/movement-restrictor'; | ||
export * from './features/change-bounds/snap'; | ||
export * from './features/context-actions/action-definitions'; | ||
export * from './features/command-palette/server-command-palette-provider'; | ||
export * from './features/context-menu/mouse-listener'; | ||
export * from './features/context-menu/context-menu-service'; | ||
export * from './features/context-menu/menu-providers'; | ||
export * from './features/edit-label-validation/edit-label-validator'; | ||
@@ -55,3 +53,2 @@ export * from './features/execute/execute-command'; | ||
export * from './features/rank/model'; | ||
export * from './features/reconnect/action-definitions'; | ||
export * from './features/reconnect/model'; | ||
@@ -63,3 +60,3 @@ export * from './features/request-response/glsp-action-dispatcher'; | ||
export * from './features/tool-feedback/creation-tool-feedback'; | ||
export * from './features/tool-feedback/cursor-feedback'; | ||
export * from './features/tool-feedback/css-feedback'; | ||
export * from './features/tool-feedback/edge-edit-tool-feedback'; | ||
@@ -66,0 +63,0 @@ export * from './features/tool-feedback/feedback-action-dispatcher'; |
@@ -86,3 +86,3 @@ /******************************************************************************** | ||
registry.register(OperationKind.RECONNECT_CONNECTION, diagramServer); | ||
registry.register(OperationKind.REROUTE_CONNECTION, diagramServer); | ||
registry.register(OperationKind.CHANGE_ROUTING_POINTS, diagramServer); | ||
registry.register(OperationKind.CREATE_NODE, diagramServer); | ||
@@ -89,0 +89,0 @@ registry.register(OperationKind.CHANGE_BOUNDS, diagramServer); |
@@ -19,3 +19,4 @@ /******************************************************************************** | ||
isBoundsAware, | ||
isSelectable, | ||
isMoveable, | ||
isSelected, | ||
Selectable, | ||
@@ -26,2 +27,3 @@ SModelElement, | ||
} from "sprotty/lib"; | ||
import { ElementAndRoutingPoints } from "src/features/operation/operation-actions"; | ||
@@ -57,6 +59,2 @@ | ||
export function isSelected(element: SModelElement | undefined): element is SModelElement & Selectable { | ||
return isNotUndefined(element) && isSelectable(element) && element.selected; | ||
} | ||
export function isNotUndefined<T>(element: T | undefined): element is T { | ||
@@ -89,2 +87,6 @@ return element !== undefined; | ||
export function isNonRoutableSelectedMovableBoundsAware(element: SModelElement): element is SelectableBoundsAware { | ||
return isNonRoutableSelectedBoundsAware(element) && isMoveable(element); | ||
} | ||
export function isNonRoutableSelectedBoundsAware(element: SModelElement): element is SelectableBoundsAware { | ||
@@ -117,1 +119,8 @@ return isBoundsAware(element) && isSelected(element) && !isRoutable(element); | ||
} | ||
export function toElementAndRoutingPoints(element: SRoutableElement): ElementAndRoutingPoints { | ||
return { | ||
elementId: element.id, | ||
newRoutingPoints: element.routingPoints | ||
}; | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
10
970258
13
346
12
15158