diff --git a/src/canvas.class.js b/src/canvas.class.js index 9177bb42fe7..1b1ab993283 100644 --- a/src/canvas.class.js +++ b/src/canvas.class.js @@ -178,6 +178,13 @@ */ selectionLineWidth: 1, + /** + * Select only shapes that are fully contained in the dragged selection rectangle. + * @type Boolean + * @default + */ + selectionFullyContained: false, + /** * Default cursor value used when hovering over an object on canvas * @type String diff --git a/src/mixins/canvas_grouping.mixin.js b/src/mixins/canvas_grouping.mixin.js index 9806b6e105b..6a91612b3d0 100644 --- a/src/mixins/canvas_grouping.mixin.js +++ b/src/mixins/canvas_grouping.mixin.js @@ -124,6 +124,7 @@ y2 = y1 + this._groupSelector.top, selectionX1Y1 = new fabric.Point(min(x1, x2), min(y1, y2)), selectionX2Y2 = new fabric.Point(max(x1, x2), max(y1, y2)), + allowIntersect = !this.selectionFullyContained, isClick = x1 === x2 && y1 === y2; // we iterate reverse order to collect top first in case of click. for (var i = this._objects.length; i--; ) { @@ -133,10 +134,10 @@ continue; } - if (currentObject.intersectsWithRect(selectionX1Y1, selectionX2Y2) || + if ((allowIntersect && currentObject.intersectsWithRect(selectionX1Y1, selectionX2Y2)) || currentObject.isContainedWithinRect(selectionX1Y1, selectionX2Y2) || - currentObject.containsPoint(selectionX1Y1) || - currentObject.containsPoint(selectionX2Y2) + (allowIntersect && currentObject.containsPoint(selectionX1Y1)) || + (allowIntersect && currentObject.containsPoint(selectionX2Y2)) ) { group.push(currentObject); diff --git a/test/unit/canvas.js b/test/unit/canvas.js index 64f1e87bd93..0ece12bc849 100644 --- a/test/unit/canvas.js +++ b/test/unit/canvas.js @@ -531,6 +531,47 @@ assert.equal(collected[2], rect1, 'rect1 is collected'); }); + QUnit.test('_collectObjects collects object fully contained in area', function(assert) { + canvas.selectionFullyContained = true; + var rect1 = new fabric.Rect({ width: 10, height: 10, top: 0, left: 0 }); + var rect2 = new fabric.Rect({ width: 10, height: 10, top: 0, left: 10 }); + var rect3 = new fabric.Rect({ width: 10, height: 10, top: 10, left: 0 }); + var rect4 = new fabric.Rect({ width: 10, height: 10, top: 10, left: 10 }); + canvas.add(rect1, rect2, rect3, rect4); + canvas._groupSelector = { + top: 30, + left: 30, + ex: -1, + ey: -1 + }; + var collected = canvas._collectObjects(); + assert.equal(collected.length, 4, 'a rect that contains all objects collects them all'); + assert.equal(collected[3], rect1, 'contains rect1 as last object'); + assert.equal(collected[2], rect2, 'contains rect2'); + assert.equal(collected[1], rect3, 'contains rect3'); + assert.equal(collected[0], rect4, 'contains rect4 as first object'); + canvas.selectionFullyContained = false; + }); + + QUnit.test('_collectObjects does not collect objects not fully contained', function(assert) { + canvas.selectionFullyContained = true; + var rect1 = new fabric.Rect({ width: 10, height: 10, top: 0, left: 0 }); + var rect2 = new fabric.Rect({ width: 10, height: 10, top: 0, left: 10 }); + var rect3 = new fabric.Rect({ width: 10, height: 10, top: 10, left: 0 }); + var rect4 = new fabric.Rect({ width: 10, height: 10, top: 10, left: 10 }); + canvas.add(rect1, rect2, rect3, rect4); + canvas._groupSelector = { + top: 20, + left: 20, + ex: 5, + ey: 5 + }; + var collected = canvas._collectObjects(); + assert.equal(collected.length, 1, 'a rect intersecting objects does not collect those'); + assert.equal(collected[0], rect4, 'contains rect1 as only one fully contained'); + canvas.selectionFullyContained = false; + }); + QUnit.test('_fireSelectionEvents fires multiple things', function(assert) { var rect1Deselected = false; var rect3Selected = false;