| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615 |
- import {
- assign,
- filter,
- forEach,
- isNumber
- } from 'min-dash';
- import {
- asTRBL,
- getMid
- } from '../../layout/LayoutUtil';
- import { getBBox } from '../../util/Elements';
- import { getDirection } from './SpaceUtil';
- import { hasPrimaryModifier } from '../../util/Mouse';
- import { set as setCursor } from '../../util/Cursor';
- import { selfAndAllChildren } from '../../util/Elements';
- var abs = Math.abs,
- round = Math.round;
- var AXIS_TO_DIMENSION = {
- x: 'width',
- y: 'height'
- };
- var CURSOR_CROSSHAIR = 'crosshair';
- var DIRECTION_TO_TRBL = {
- n: 'top',
- w: 'left',
- s: 'bottom',
- e: 'right'
- };
- var HIGH_PRIORITY = 1500;
- var DIRECTION_TO_OPPOSITE = {
- n: 's',
- w: 'e',
- s: 'n',
- e: 'w'
- };
- var PADDING = 20;
- /**
- * Add or remove space by moving and resizing elements.
- *
- * @param {Canvas} canvas
- * @param {Dragging} dragging
- * @param {EventBus} eventBus
- * @param {Modeling} modeling
- * @param {Rules} rules
- * @param {ToolManager} toolManager
- * @param {Mouse} mouse
- */
- export default function SpaceTool(
- canvas, dragging, eventBus,
- modeling, rules, toolManager,
- mouse) {
- this._canvas = canvas;
- this._dragging = dragging;
- this._eventBus = eventBus;
- this._modeling = modeling;
- this._rules = rules;
- this._toolManager = toolManager;
- this._mouse = mouse;
- var self = this;
- toolManager.registerTool('space', {
- tool: 'spaceTool.selection',
- dragging: 'spaceTool'
- });
- eventBus.on('spaceTool.selection.end', function(event) {
- eventBus.once('spaceTool.selection.ended', function() {
- self.activateMakeSpace(event.originalEvent);
- });
- });
- eventBus.on('spaceTool.move', HIGH_PRIORITY , function(event) {
- var context = event.context,
- initialized = context.initialized;
- if (!initialized) {
- initialized = context.initialized = self.init(event, context);
- }
- if (initialized) {
- ensureConstraints(event);
- }
- });
- eventBus.on('spaceTool.end', function(event) {
- var context = event.context,
- axis = context.axis,
- direction = context.direction,
- movingShapes = context.movingShapes,
- resizingShapes = context.resizingShapes,
- start = context.start;
- if (!context.initialized) {
- return;
- }
- ensureConstraints(event);
- var delta = {
- x: 0,
- y: 0
- };
- delta[ axis ] = round(event[ 'd' + axis ]);
- self.makeSpace(movingShapes, resizingShapes, delta, direction, start);
- eventBus.once('spaceTool.ended', function(event) {
- // activate space tool selection after make space
- self.activateSelection(event.originalEvent, true, true);
- });
- });
- }
- SpaceTool.$inject = [
- 'canvas',
- 'dragging',
- 'eventBus',
- 'modeling',
- 'rules',
- 'toolManager',
- 'mouse'
- ];
- /**
- * Activate space tool selection.
- *
- * @param {Object} event
- * @param {boolean} autoActivate
- */
- SpaceTool.prototype.activateSelection = function(event, autoActivate, reactivate) {
- this._dragging.init(event, 'spaceTool.selection', {
- autoActivate: autoActivate,
- cursor: CURSOR_CROSSHAIR,
- data: {
- context: {
- reactivate: reactivate
- }
- },
- trapClick: false
- });
- };
- /**
- * Activate space tool make space.
- *
- * @param {MouseEvent} event
- */
- SpaceTool.prototype.activateMakeSpace = function(event) {
- this._dragging.init(event, 'spaceTool', {
- autoActivate: true,
- cursor: CURSOR_CROSSHAIR,
- data: {
- context: {}
- }
- });
- };
- /**
- * Make space.
- *
- * @param {Array<djs.model.Shape>} movingShapes
- * @param {Array<djs.model.Shape>} resizingShapes
- * @param {Object} delta
- * @param {number} delta.x
- * @param {number} delta.y
- * @param {string} direction
- * @param {number} start
- */
- SpaceTool.prototype.makeSpace = function(movingShapes, resizingShapes, delta, direction, start) {
- return this._modeling.createSpace(movingShapes, resizingShapes, delta, direction, start);
- };
- /**
- * Initialize make space and return true if that was successful.
- *
- * @param {Object} event
- * @param {Object} context
- *
- * @return {boolean}
- */
- SpaceTool.prototype.init = function(event, context) {
- var axis = abs(event.dx) > abs(event.dy) ? 'x' : 'y',
- delta = event[ 'd' + axis ],
- start = event[ axis ] - delta;
- if (abs(delta) < 5) {
- return false;
- }
- // invert delta to remove space when moving left
- if (delta < 0) {
- delta *= -1;
- }
- // invert delta to add/remove space when removing/adding space if modifier key is pressed
- if (hasPrimaryModifier(event)) {
- delta *= -1;
- }
- var direction = getDirection(axis, delta);
- var root = this._canvas.getRootElement();
- var children = selfAndAllChildren(root, true);
- var elements = this.calculateAdjustments(children, axis, delta, start);
- var minDimensions = this._eventBus.fire('spaceTool.getMinDimensions', {
- axis: axis,
- direction: direction,
- shapes: elements.resizingShapes,
- start: start
- });
- var spaceToolConstraints = getSpaceToolConstraints(elements, axis, direction, start, minDimensions);
- assign(
- context,
- elements,
- {
- axis: axis,
- direction: direction,
- spaceToolConstraints: spaceToolConstraints,
- start: start
- }
- );
- setCursor('resize-' + (axis === 'x' ? 'ew' : 'ns'));
- return true;
- };
- /**
- * Get elements to be moved and resized.
- *
- * @param {Array<djs.model.Shape>} elements
- * @param {string} axis
- * @param {number} delta
- * @param {number} start
- *
- * @return {Object}
- */
- SpaceTool.prototype.calculateAdjustments = function(elements, axis, delta, start) {
- var rules = this._rules;
- var movingShapes = [],
- resizingShapes = [];
- var attachers = [],
- connections = [];
- function moveShape(shape) {
- if (!movingShapes.includes(shape)) {
- movingShapes.push(shape);
- }
- var label = shape.label;
- // move external label if its label target is moving
- if (label && !movingShapes.includes(label)) {
- movingShapes.push(label);
- }
- }
- function resizeShape(shape) {
- if (!resizingShapes.includes(shape)) {
- resizingShapes.push(shape);
- }
- }
- forEach(elements, function(element) {
- if (!element.parent || isLabel(element)) {
- return;
- }
- // handle connections separately
- if (isConnection(element)) {
- connections.push(element);
- return;
- }
- var shapeStart = element[ axis ],
- shapeEnd = shapeStart + element[ AXIS_TO_DIMENSION[ axis ] ];
- // handle attachers separately
- if (isAttacher(element)
- && ((delta > 0 && getMid(element)[ axis ] > start)
- || (delta < 0 && getMid(element)[ axis ] < start))) {
- attachers.push(element);
- return;
- }
- // move shape if its start is after space tool
- if ((delta > 0 && shapeStart > start)
- || (delta < 0 && shapeEnd < start)) {
- moveShape(element);
- return;
- }
- // resize shape if it's resizable and its start is before and its end is after space tool
- if (shapeStart < start
- && shapeEnd > start
- && rules.allowed('shape.resize', { shape: element })
- ) {
- resizeShape(element);
- return;
- }
- });
- // move attacher if its host is moving
- forEach(movingShapes, function(shape) {
- var attachers = shape.attachers;
- if (attachers) {
- forEach(attachers, function(attacher) {
- moveShape(attacher);
- });
- }
- });
- var allShapes = movingShapes.concat(resizingShapes);
- // move attacher if its mid is after space tool and its host is moving or resizing
- forEach(attachers, function(attacher) {
- var host = attacher.host;
- if (includes(allShapes, host)) {
- moveShape(attacher);
- }
- });
- allShapes = movingShapes.concat(resizingShapes);
- // move external label if its label target's (connection) source and target are moving
- forEach(connections, function(connection) {
- var source = connection.source,
- target = connection.target,
- label = connection.label;
- if (includes(allShapes, source)
- && includes(allShapes, target)
- && label) {
- moveShape(label);
- }
- });
- return {
- movingShapes: movingShapes,
- resizingShapes: resizingShapes
- };
- };
- SpaceTool.prototype.toggle = function() {
- if (this.isActive()) {
- return this._dragging.cancel();
- }
- var mouseEvent = this._mouse.getLastMoveEvent();
- this.activateSelection(mouseEvent, !!mouseEvent);
- };
- SpaceTool.prototype.isActive = function() {
- var context = this._dragging.context();
- if (context) {
- return /^spaceTool/.test(context.prefix);
- }
- return false;
- };
- // helpers //////////
- function addPadding(trbl) {
- return {
- top: trbl.top - PADDING,
- right: trbl.right + PADDING,
- bottom: trbl.bottom + PADDING,
- left: trbl.left - PADDING
- };
- }
- function ensureConstraints(event) {
- var context = event.context,
- spaceToolConstraints = context.spaceToolConstraints;
- if (!spaceToolConstraints) {
- return;
- }
- var x, y;
- if (isNumber(spaceToolConstraints.left)) {
- x = Math.max(event.x, spaceToolConstraints.left);
- event.dx = event.dx + x - event.x;
- event.x = x;
- }
- if (isNumber(spaceToolConstraints.right)) {
- x = Math.min(event.x, spaceToolConstraints.right);
- event.dx = event.dx + x - event.x;
- event.x = x;
- }
- if (isNumber(spaceToolConstraints.top)) {
- y = Math.max(event.y, spaceToolConstraints.top);
- event.dy = event.dy + y - event.y;
- event.y = y;
- }
- if (isNumber(spaceToolConstraints.bottom)) {
- y = Math.min(event.y, spaceToolConstraints.bottom);
- event.dy = event.dy + y - event.y;
- event.y = y;
- }
- }
- function getSpaceToolConstraints(elements, axis, direction, start, minDimensions) {
- var movingShapes = elements.movingShapes,
- resizingShapes = elements.resizingShapes;
- if (!resizingShapes.length) {
- return;
- }
- var spaceToolConstraints = {},
- min,
- max;
- forEach(resizingShapes, function(resizingShape) {
- var attachers = resizingShape.attachers,
- children = resizingShape.children;
- var resizingShapeBBox = asTRBL(resizingShape);
- // find children that are not moving or resizing
- var nonMovingResizingChildren = filter(children, function(child) {
- return !isConnection(child) &&
- !isLabel(child) &&
- !includes(movingShapes, child) &&
- !includes(resizingShapes, child);
- });
- // find children that are moving
- var movingChildren = filter(children, function(child) {
- return !isConnection(child) && !isLabel(child) && includes(movingShapes, child);
- });
- var minOrMax,
- nonMovingResizingChildrenBBox,
- movingChildrenBBox,
- movingAttachers = [],
- nonMovingAttachers = [],
- movingAttachersBBox,
- movingAttachersConstraint,
- nonMovingAttachersBBox,
- nonMovingAttachersConstraint;
- if (nonMovingResizingChildren.length) {
- nonMovingResizingChildrenBBox = addPadding(asTRBL(getBBox(nonMovingResizingChildren)));
- minOrMax = start -
- resizingShapeBBox[ DIRECTION_TO_TRBL[ direction ] ] +
- nonMovingResizingChildrenBBox[ DIRECTION_TO_TRBL[ direction ] ];
- if (direction === 'n') {
- spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
- } else if (direction === 'w') {
- spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
- } else if (direction === 's') {
- spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
- } else if (direction === 'e') {
- spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
- }
- }
- if (movingChildren.length) {
- movingChildrenBBox = addPadding(asTRBL(getBBox(movingChildren)));
- minOrMax = start -
- movingChildrenBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ] +
- resizingShapeBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ];
- if (direction === 'n') {
- spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
- } else if (direction === 'w') {
- spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
- } else if (direction === 's') {
- spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
- } else if (direction === 'e') {
- spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
- }
- }
- if (attachers && attachers.length) {
- attachers.forEach(function(attacher) {
- if (includes(movingShapes, attacher)) {
- movingAttachers.push(attacher);
- } else {
- nonMovingAttachers.push(attacher);
- }
- });
- if (movingAttachers.length) {
- movingAttachersBBox = asTRBL(getBBox(movingAttachers.map(getMid)));
- movingAttachersConstraint = resizingShapeBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ]
- - (movingAttachersBBox[ DIRECTION_TO_TRBL[ DIRECTION_TO_OPPOSITE[ direction ] ] ] - start);
- }
- if (nonMovingAttachers.length) {
- nonMovingAttachersBBox = asTRBL(getBBox(nonMovingAttachers.map(getMid)));
- nonMovingAttachersConstraint = nonMovingAttachersBBox[ DIRECTION_TO_TRBL[ direction ] ]
- - (resizingShapeBBox[ DIRECTION_TO_TRBL[ direction ] ] - start);
- }
- if (direction === 'n') {
- minOrMax = Math.min(movingAttachersConstraint || Infinity, nonMovingAttachersConstraint || Infinity);
- spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
- } else if (direction === 'w') {
- minOrMax = Math.min(movingAttachersConstraint || Infinity, nonMovingAttachersConstraint || Infinity);
- spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
- } else if (direction === 's') {
- minOrMax = Math.max(movingAttachersConstraint || -Infinity, nonMovingAttachersConstraint || -Infinity);
- spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
- } else if (direction === 'e') {
- minOrMax = Math.max(movingAttachersConstraint || -Infinity, nonMovingAttachersConstraint || -Infinity);
- spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
- }
- }
- var resizingShapeMinDimensions = minDimensions && minDimensions[ resizingShape.id ];
- if (resizingShapeMinDimensions) {
- if (direction === 'n') {
- minOrMax = start +
- resizingShape[ AXIS_TO_DIMENSION [ axis ] ] -
- resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
- spaceToolConstraints.bottom = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
- } else if (direction === 'w') {
- minOrMax = start +
- resizingShape[ AXIS_TO_DIMENSION [ axis ] ] -
- resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
- spaceToolConstraints.right = max = isNumber(max) ? Math.min(max, minOrMax) : minOrMax;
- } else if (direction === 's') {
- minOrMax = start -
- resizingShape[ AXIS_TO_DIMENSION [ axis ] ] +
- resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
- spaceToolConstraints.top = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
- } else if (direction === 'e') {
- minOrMax = start -
- resizingShape[ AXIS_TO_DIMENSION [ axis ] ] +
- resizingShapeMinDimensions[ AXIS_TO_DIMENSION[ axis ] ];
- spaceToolConstraints.left = min = isNumber(min) ? Math.max(min, minOrMax) : minOrMax;
- }
- }
- });
- return spaceToolConstraints;
- }
- function includes(array, item) {
- return array.indexOf(item) !== -1;
- }
- function isAttacher(element) {
- return !!element.host;
- }
- function isConnection(element) {
- return !!element.waypoints;
- }
- function isLabel(element) {
- return !!element.labelTarget;
- }
|