Implement ChangeFlag.INSERTION notification to know when an item was inserted in a new parent inside the DOM.

This commit is contained in:
Jürg Lehni 2014-03-18 15:28:29 +01:00
parent 946e0d5b2c
commit aeeba4d58d
3 changed files with 43 additions and 21 deletions

View file

@ -14,25 +14,27 @@ var ChangeFlag = {
// Anything affecting the appearance of an item, including GEOMETRY, // Anything affecting the appearance of an item, including GEOMETRY,
// STROKE, STYLE and ATTRIBUTE (except for the invisible ones: locked, name) // STROKE, STYLE and ATTRIBUTE (except for the invisible ones: locked, name)
APPEARANCE: 0x1, APPEARANCE: 0x1,
// Change in item hierarchy // A change in the item's children
CHILDREN: 0x2, CHILDREN: 0x2,
// A change in the item's place in the DOM (removed, inserted, moved).
INSERTION: 0x4,
// Item geometry (path, bounds) // Item geometry (path, bounds)
GEOMETRY: 0x4, GEOMETRY: 0x8,
// Only segment(s) have changed, and affected curves have alredy been // Only segment(s) have changed, and affected curves have already been
// notified. This is to implement an optimization in _changed() calls. // notified. This is to implement an optimization in _changed() calls.
SEGMENTS: 0x8, SEGMENTS: 0x10,
// Stroke geometry (excluding color) // Stroke geometry (excluding color)
STROKE: 0x10, STROKE: 0x20,
// Fill style or stroke color / dash // Fill style or stroke color / dash
STYLE: 0x20, STYLE: 0x40,
// Item attributes: visible, blendMode, locked, name, opacity, clipMask ... // Item attributes: visible, blendMode, locked, name, opacity, clipMask ...
ATTRIBUTE: 0x40, ATTRIBUTE: 0x80,
// Text content // Text content
CONTENT: 0x80, CONTENT: 0x100,
// Raster pixels // Raster pixels
PIXELS: 0x100, PIXELS: 0x200,
// Clipping in one of the child items // Clipping in one of the child items
CLIPPING: 0x200 CLIPPING: 0x400
}; };
// Shortcuts to often used ChangeFlag values including APPEARANCE // Shortcuts to often used ChangeFlag values including APPEARANCE
@ -40,6 +42,8 @@ var Change = {
// CHILDREN also changes GEOMETRY, since removing children from groups // CHILDREN also changes GEOMETRY, since removing children from groups
// changes bounds. // changes bounds.
CHILDREN: ChangeFlag.CHILDREN | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE, CHILDREN: ChangeFlag.CHILDREN | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
// Changing the insertion can change the appearance through parent's matrix.
INSERTION: ChangeFlag.INSERTION | ChangeFlag.APPEARANCE,
GEOMETRY: ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE, GEOMETRY: ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
SEGMENTS: ChangeFlag.SEGMENTS | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE, SEGMENTS: ChangeFlag.SEGMENTS | ChangeFlag.GEOMETRY | ChangeFlag.APPEARANCE,
STROKE: ChangeFlag.STROKE | ChangeFlag.STYLE | ChangeFlag.APPEARANCE, STROKE: ChangeFlag.STROKE | ChangeFlag.STYLE | ChangeFlag.APPEARANCE,

View file

@ -2006,10 +2006,15 @@ var Item = Base.extend(Callback, /** @lends Item# */{
if (_proto && !(item instanceof _proto)) { if (_proto && !(item instanceof _proto)) {
items.splice(i, 1); items.splice(i, 1);
} else { } else {
item._remove(true); // Notify parent of change. Don't notify item itself yet,
// as we're doing so when adding it to the new parent below.
item._remove(false, true);
} }
} }
Base.splice(children, items, index, 0); Base.splice(children, items, index, 0);
var project = this._project,
// See #_remove() for an explanation of this:
notifySelf = project && project._changes;
for (var i = 0, l = items.length; i < l; i++) { for (var i = 0, l = items.length; i < l; i++) {
var item = items[i]; var item = items[i];
item._parent = this; item._parent = this;
@ -2018,6 +2023,8 @@ var Item = Base.extend(Callback, /** @lends Item# */{
// are kept in sync. // are kept in sync.
if (item._name) if (item._name)
item.setName(item._name); item.setName(item._name);
if (notifySelf)
this._changed(/*#=*/ Change.INSERTION);
} }
this._changed(/*#=*/ Change.CHILDREN); this._changed(/*#=*/ Change.CHILDREN);
} else { } else {
@ -2164,16 +2171,24 @@ var Item = Base.extend(Callback, /** @lends Item# */{
/** /**
* Removes the item from its parent's children list. * Removes the item from its parent's children list.
*/ */
_remove: function(notify) { _remove: function(notifySelf, notifyParent) {
if (this._parent) { var parent = this._parent;
if (parent) {
if (this._name) if (this._name)
this._removeNamed(); this._removeNamed();
if (this._index != null) if (this._index != null)
Base.splice(this._parent._children, null, this._index, 1); Base.splice(parent._children, null, this._index, 1);
this._installEvents(false); this._installEvents(false);
// Notify parent of changed hierarchy // Notify self of the insertion change. We only need this
if (notify) // notification if we're tracking changes for now.
this._parent._changed(/*#=*/ Change.CHILDREN); if (notifySelf) {
var project = this._project;
if (project && project._changes)
this._changed(/*#=*/ Change.INSERTION);
}
// Notify parent of changed children
if (notifyParent)
parent._changed(/*#=*/ Change.CHILDREN);
this._parent = null; this._parent = null;
return true; return true;
} }
@ -2187,7 +2202,8 @@ var Item = Base.extend(Callback, /** @lends Item# */{
* @return {Boolean} {@true if the item was removed} * @return {Boolean} {@true if the item was removed}
*/ */
remove: function() { remove: function() {
return this._remove(true); // Notify self and parent of change:
return this._remove(true, true);
}, },
/** /**
@ -2217,8 +2233,10 @@ var Item = Base.extend(Callback, /** @lends Item# */{
// deletes it for the removed items. Calling #_remove() afterwards is // deletes it for the removed items. Calling #_remove() afterwards is
// fine, since it only calls Base.splice() if #_index is set. // fine, since it only calls Base.splice() if #_index is set.
var removed = Base.splice(this._children, null, from, to - from); var removed = Base.splice(this._children, null, from, to - from);
for (var i = removed.length - 1; i >= 0; i--) for (var i = removed.length - 1; i >= 0; i--) {
removed[i]._remove(false); // Don't notify parent each time, notify it separately after.
removed[i]._remove(true, false);
}
if (removed.length > 0) if (removed.length > 0)
this._changed(/*#=*/ Change.CHILDREN); this._changed(/*#=*/ Change.CHILDREN);
return removed; return removed;

View file

@ -131,7 +131,7 @@ var Layer = Group.extend(/** @lends Layer# */{
// If the item is a layer and contained within Project#layers, use // If the item is a layer and contained within Project#layers, use
// our own version of move(). // our own version of move().
if (item instanceof Layer && !item._parent) { if (item instanceof Layer && !item._parent) {
this._remove(true); this._remove(true, true);
Base.splice(item._project.layers, [this], Base.splice(item._project.layers, [this],
item._index + (above ? 1 : 0), 0); item._index + (above ? 1 : 0), 0);
this._setProject(item._project, true); this._setProject(item._project, true);