-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
fix(ActiveSelection): 🚀 multiple nested selection 🗂️ #7882
Changes from all commits
1a191a7
26e8392
7d793b3
2708029
02a8ffa
0d3690f
ff0346a
812f02a
053946c
db76c44
04aad5b
8c1c535
3fd4af2
58e9be2
c2ef2f5
f0145b5
cb20b81
347f80d
a56d55e
2a426e7
5e3fc1f
2b1f680
1ad5cc8
11c828f
b6fa3dc
e9afc89
d8f8251
c58a274
c0f5571
9a223d0
2d5cc75
01c4a87
2164161
a5575bd
5c56124
5e3aaf5
d53d6b6
8e907bb
f082fc9
c2d93db
2f346b0
9931a3c
d6f1950
5cb64af
63cab25
21110d3
6c141e1
465ea96
ffafe66
e2d03f0
3e09114
af3430c
67abe2f
b8477f2
d039d04
2c49f67
7ba7fa4
de96660
b566158
beaa331
c0aea33
3ec53b4
1c414cf
2c65a5c
785433a
56a2b73
3df9435
d49baac
31d2107
3f837ce
dd9f194
014de45
9a48c10
51a454c
020c2bb
6d0ed73
d18351f
de46f45
a456afe
9214d8b
9665b54
fba43bd
8c095a3
11da63b
6d5df90
8377693
08f3c83
e23471c
b9a32d5
dbc2e7d
f34ccb3
0fc26e3
f642a28
2c78fb5
245a796
84c3345
79db7d5
24c279f
91232c8
7b479ca
fc48a27
9d77261
5f3a39f
864f174
8c317fe
91a7f53
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -52,13 +52,49 @@ | |||||
this.setCoords(); | ||||||
}, | ||||||
|
||||||
/** | ||||||
* @private | ||||||
* @param {string} key | ||||||
* @param {*} value | ||||||
*/ | ||||||
_set: function (key, value) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is the only part of active selection that hasn't been ported to #8665 |
||||||
var prev = this[key]; | ||||||
this.callSuper('_set', key, value); | ||||||
if (prev !== this[key] && this.canvas && this.canvas.preserveObjectStacking | ||||||
&& this.stateProperties.indexOf(key) > -1) { | ||||||
var invalidationContext = { | ||||||
target: this, | ||||||
key: key, | ||||||
value: value, | ||||||
prevValue: prev | ||||||
}; | ||||||
var invalidatedGroups = []; | ||||||
this.forEachObject(function (object) { | ||||||
var group = object.__owningGroup; | ||||||
if (group && invalidatedGroups.indexOf(group) === -1) { | ||||||
group.invalidate(invalidationContext); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe I want to add a check for objects that are group de-facto but aren't really groups:
Suggested change
|
||||||
invalidatedGroups.push(group); | ||||||
} | ||||||
}); | ||||||
} | ||||||
return this; | ||||||
}, | ||||||
|
||||||
/** | ||||||
* @private | ||||||
*/ | ||||||
_shouldSetNestedCoords: function () { | ||||||
return true; | ||||||
}, | ||||||
|
||||||
/** | ||||||
* @private | ||||||
* @override we don't want the selection monitor to be active | ||||||
*/ | ||||||
__objectSelectionMonitor: function () { | ||||||
// noop | ||||||
}, | ||||||
|
||||||
/** | ||||||
* @private | ||||||
* @param {fabric.Object} object | ||||||
|
@@ -69,7 +105,6 @@ | |||||
if (object.group) { | ||||||
// save ref to group for later in order to return to it | ||||||
var parent = object.group; | ||||||
parent._exitGroup(object); | ||||||
object.__owningGroup = parent; | ||||||
} | ||||||
this._enterGroup(object, removeParentTransform); | ||||||
|
@@ -87,7 +122,7 @@ | |||||
var parent = object.__owningGroup; | ||||||
if (parent) { | ||||||
// return to owning group | ||||||
parent.enterGroup(object); | ||||||
parent._enterGroup(object, true); | ||||||
delete object.__owningGroup; | ||||||
} | ||||||
}, | ||||||
|
@@ -98,6 +133,7 @@ | |||||
* @param {fabric.Object[]} targets | ||||||
*/ | ||||||
_onAfterObjectsChange: function (type, targets) { | ||||||
this.callSuper('_onAfterObjectsChange', type, targets); | ||||||
var groups = []; | ||||||
targets.forEach(function (object) { | ||||||
object.group && !groups.includes(object.group) && groups.push(object.group); | ||||||
|
@@ -116,6 +152,14 @@ | |||||
} | ||||||
}, | ||||||
|
||||||
/** | ||||||
* @override there's no reason to invalidate because objects aren't selectable | ||||||
* @private | ||||||
*/ | ||||||
invalidate: function () { | ||||||
// noop | ||||||
}, | ||||||
|
||||||
/** | ||||||
* If returns true, deselection is cancelled. | ||||||
* @since 2.0.0 | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -257,6 +257,11 @@ | |
object.group.remove(object); | ||
} | ||
this._enterGroup(object, removeParentTransform); | ||
var activeObject = this.canvas && this.canvas.getActiveObject && this.canvas.getActiveObject(); | ||
// if we are adding the activeObject in a group | ||
if (activeObject && (activeObject === object || object.isDescendantOf(activeObject))) { | ||
this._activeObjects.push(object); | ||
} | ||
Comment on lines
+260
to
+264
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this part....\ |
||
return true; | ||
}, | ||
|
||
|
@@ -280,11 +285,6 @@ | |
object._set('group', this); | ||
object._set('canvas', this.canvas); | ||
this.interactive && this._watchObject(true, object); | ||
var activeObject = this.canvas && this.canvas.getActiveObject && this.canvas.getActiveObject(); | ||
// if we are adding the activeObject in a group | ||
if (activeObject && (activeObject === object || object.isDescendantOf(activeObject))) { | ||
this._activeObjects.push(object); | ||
} | ||
}, | ||
|
||
/** | ||
|
@@ -410,35 +410,23 @@ | |
* Execute the drawing operation for an object on a specified context | ||
* @param {CanvasRenderingContext2D} ctx Context to render on | ||
*/ | ||
drawObject: function(ctx) { | ||
drawObject: function (ctx) { | ||
this._renderBackground(ctx); | ||
for (var i = 0; i < this._objects.length; i++) { | ||
this._objects[i].render(ctx); | ||
} | ||
this._drawClipPath(ctx, this.clipPath); | ||
}, | ||
|
||
/** | ||
* Check if cache is dirty | ||
*/ | ||
isCacheDirty: function(skipCanvas) { | ||
if (this.callSuper('isCacheDirty', skipCanvas)) { | ||
return true; | ||
} | ||
if (!this.statefullCache) { | ||
return false; | ||
} | ||
for (var i = 0; i < this._objects.length; i++) { | ||
if (this._objects[i].isCacheDirty(true)) { | ||
if (this._cacheCanvas) { | ||
// if this group has not a cache canvas there is nothing to clean | ||
var x = this.cacheWidth / this.zoomX, y = this.cacheHeight / this.zoomY; | ||
this._cacheContext.clearRect(-x / 2, -y / 2, x, y); | ||
} | ||
return true; | ||
var preserveObjectStacking = this.canvas && this.canvas.preserveObjectStacking; | ||
for (var i = 0, object; i < this._objects.length; i++) { | ||
object = this._objects[i]; | ||
if (preserveObjectStacking && object.group !== this) { | ||
// object is part of ActiveSelection | ||
ctx.save(); | ||
ctx.transform.apply(ctx, invertTransform(this.calcTransformMatrix())); | ||
object.render(ctx); | ||
ctx.restore(); | ||
} | ||
else if (preserveObjectStacking || (object.group === this && this._activeObjects.indexOf(object) === -1)) { | ||
object.render(ctx); | ||
} | ||
} | ||
return false; | ||
this._drawClipPath(ctx, this.clipPath); | ||
}, | ||
|
||
/** | ||
|
@@ -463,6 +451,24 @@ | |
this._transformDone = false; | ||
}, | ||
|
||
/** | ||
* @typedef {object} InvalidationContext | ||
* @property {fabric.Object} target either a child object or {@link fabric.ActiveSelection} | ||
* @property {string} key | ||
* @property {*} value | ||
* @property {*} prevValue | ||
* | ||
* @private | ||
* @param {InvalidationContext} context | ||
*/ | ||
invalidate: function (context) { | ||
this.isOnACache() && (!this.canvas || this.canvas.preserveObjectStacking) | ||
&& this._set('dirty', true); | ||
this._applyLayoutStrategy(Object.assign({}, context, { | ||
type: 'progress' | ||
})); | ||
}, | ||
|
||
/** | ||
* @public | ||
* @param {Partial<LayoutResult> & { layout?: string }} [context] pass values to use for layout calculations | ||
|
@@ -501,9 +507,14 @@ | |
// reject layout requests before initialization layout | ||
return; | ||
} | ||
else if (context.type === 'progress' && this._layoutInProgress) { | ||
// prevent circular calls | ||
return; | ||
} | ||
var center = this.getRelativeCenterPoint(); | ||
var result = this.getLayoutStrategyResult(this.layout, this._objects.concat(), context); | ||
if (result) { | ||
this._layoutInProgress = true; | ||
// handle positioning | ||
var newCenter = new fabric.Point(result.centerX, result.centerY); | ||
var vector = center.subtract(newCenter).add(new fabric.Point(result.correctionX || 0, result.correctionY || 0)); | ||
|
@@ -512,7 +523,7 @@ | |
this.set({ width: result.width, height: result.height }); | ||
// adjust objects to account for new center | ||
!context.objectsRelativeToGroup && this.forEachObject(function (object) { | ||
this._adjustObjectPosition(object, diff); | ||
object.group === this && this._adjustObjectPosition(object, diff); | ||
}, this); | ||
// clip path as well | ||
!isFirstLayout && this.layout !== 'clip-path' && this.clipPath && !this.clipPath.absolutePositioned | ||
|
@@ -522,6 +533,7 @@ | |
this.setPositionByOrigin(newCenter, 'center', 'center'); | ||
this.setCoords(); | ||
} | ||
this._layoutInProgress = false; | ||
} | ||
else if (isFirstLayout) { | ||
// fill `result` with initial values for the layout hook | ||
|
@@ -583,9 +595,13 @@ | |
* @returns {LayoutResult | undefined} | ||
*/ | ||
getLayoutStrategyResult: function (layoutDirective, objects, context) { // eslint-disable-line no-unused-vars | ||
if (context.type === 'progress' | ||
/* && layoutDirective !== 'fit-content-lazy' && layoutDirective !== 'fit-content'*/) { | ||
return; | ||
} | ||
// `fit-content-lazy` performance enhancement | ||
// skip if instance had no objects before the `added` event because it may have kept layout after removing all previous objects | ||
if (layoutDirective === 'fit-content-lazy' | ||
else if (layoutDirective === 'fit-content-lazy' | ||
&& context.type === 'added' && objects.length > context.targets.length) { | ||
// calculate added objects' bbox with existing bbox | ||
var addedObjects = context.targets.concat(this); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can stop this foolishness by doing what #8034 does on Group
I didn't touch ActiveSelection because of this piece of logic but I don't see a reason why not if we want to drop this ugly part.