Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Space Tool Improvements 1/2 #480

Merged
merged 10 commits into from
Oct 25, 2022
4 changes: 1 addition & 3 deletions lib/features/modeling/cmd/SpaceToolHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,7 @@ SpaceToolHandler.prototype.updateConnectionWaypoints = function(
target = connection.target,
waypoints = copyWaypoints(connection),
axis = getAxisFromDirection(direction),
layoutHints = {
labelBehavior: false
};
layoutHints = {};

if (includes(affectedShapes, source) && includes(affectedShapes, target)) {

Expand Down
170 changes: 156 additions & 14 deletions lib/features/space-tool/SpaceTool.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import {
isNumber
} from 'min-dash';

import { asTRBL } from '../../layout/LayoutUtil';
import {
asTRBL,
getMid
} from '../../layout/LayoutUtil';

import { getBBox } from '../../util/Elements';

Expand Down Expand Up @@ -262,26 +265,105 @@ SpaceTool.prototype.calculateAdjustments = function(elements, axis, delta, start
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 || isConnection(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 ] ];

// shape to be moved
if ((delta > 0 && shapeStart > start) || (delta < 0 && shapeEnd < start)) {
return movingShapes.push(element);
// 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;
}

// shape to be resized
if (shapeStart < start &&
shapeEnd > start &&
rules.allowed('shape.resize', { shape: element })
// 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;

return resizingShapes.push(element);
if (includes(allShapes, source)
&& includes(allShapes, target)
&& label) {
moveShape(label);
}
});

Expand All @@ -305,7 +387,11 @@ SpaceTool.prototype.toggle = function() {
SpaceTool.prototype.isActive = function() {
var context = this._dragging.context();

return context && /^spaceTool/.test(context.prefix);
if (context) {
return /^spaceTool/.test(context.prefix);
}

return false;
};

// helpers //////////
Expand Down Expand Up @@ -371,24 +457,33 @@ function getSpaceToolConstraints(elements, axis, direction, start, minDimensions
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(resizingShape.children, function(child) {
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(resizingShape.children, function(child) {
var movingChildren = filter(children, function(child) {
return !isConnection(child) && !isLabel(child) && includes(movingShapes, child);
});

var minOrMax,
nonMovingResizingChildrenBBox,
movingChildrenBBox;
movingChildrenBBox,
movingAttachers = [],
nonMovingAttachers = [],
movingAttachersBBox,
movingAttachersConstraint,
nonMovingAttachersBBox,
nonMovingAttachersConstraint;

if (nonMovingResizingChildren.length) {
nonMovingResizingChildrenBBox = addPadding(asTRBL(getBBox(nonMovingResizingChildren)));
Expand Down Expand Up @@ -426,9 +521,52 @@ function getSpaceToolConstraints(elements, axis, direction, start, minDimensions
}
}

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 ] ] -
Expand Down Expand Up @@ -464,6 +602,10 @@ function includes(array, item) {
return array.indexOf(item) !== -1;
}

function isAttacher(element) {
return !!element.host;
}

function isConnection(element) {
return !!element.waypoints;
}
Expand Down
88 changes: 0 additions & 88 deletions test/spec/features/label-support/LabelSupportSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -466,92 +466,4 @@ describe('features/label-support', function() {

});


describe('space tool integration', function() {

var hostShape,
attacherShape,
attacherLabel;

var attacherLabelPos;

beforeEach(inject(function(canvas, modeling, elementFactory) {

hostShape = elementFactory.createShape({
id: 'host',
x: 500, y: 100,
width: 100, height: 100
});

canvas.addShape(hostShape, rootShape);

attacherShape = elementFactory.createShape({
id: 'attacher',
x: 525, y: 175,
width: 50, height: 50,
parent: rootShape,
host: hostShape
});

canvas.addShape(attacherShape, hostShape);

attacherLabel = elementFactory.createLabel({
id: 'attacherLabel',
width: 80, height: 40,
type: 'label'
});

modeling.createLabel(attacherShape, { x: 550, y: 250 }, attacherLabel, rootShape);

attacherLabelPos = {
x: attacherLabel.x,
y: attacherLabel.y
};
}));


describe('should move label along with its target', function() {

beforeEach(inject(function(spaceTool, dragging) {

// when
spaceTool.activateMakeSpace(canvasEvent({ x: 250, y: 100 }));

dragging.move(canvasEvent({ x: 250, y: 200 }));
dragging.end();
}));

it('execute', inject(function(spaceTool, dragging) {

// then
expect(attacherLabel.x).to.eql(attacherLabelPos.x);
expect(attacherLabel.y).to.eql(attacherLabelPos.y + 100);
}));


it('undo', inject(function(commandStack) {

// when
commandStack.undo();

// then
expect(attacherLabel.x).to.eql(attacherLabelPos.x);
expect(attacherLabel.y).to.eql(attacherLabelPos.y);
}));


it('redo', inject(function(commandStack) {

// when
commandStack.undo();
commandStack.redo();

// then
expect(attacherLabel.x).to.eql(attacherLabelPos.x);
expect(attacherLabel.y).to.eql(attacherLabelPos.y + 100);
}));
});

});

});
Loading