Skip to content

Commit

Permalink
Fix paperjs#769: Implement Item#selection flags to separate selection…
Browse files Browse the repository at this point in the history
… from item and bounds.
  • Loading branch information
lehni committed Mar 17, 2016
1 parent bb19fad commit f0edcd3
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 146 deletions.
11 changes: 3 additions & 8 deletions src/basic/Rectangle.js
Original file line number Diff line number Diff line change
Expand Up @@ -904,18 +904,13 @@ new function() {
* @default false
*/
isSelected: function() {
return this._owner._boundsSelected;
return !!(this._owner._selection & /*#=*/ItemSelection.BOUNDS);
},

setSelected: function(selected) {
var owner = this._owner;
if (owner.setSelected) {
owner._boundsSelected = selected;
// Update the owner's selected state too, so the bounds
// actually get drawn. When deselecting, take a path's
// _segmentSelection into account too, since it will
// have to remain selected even when bounds are deselected
owner.setSelected(selected || owner._segmentSelection > 0);
if (owner.changeSelection) {
owner.changeSelection(/*#=*/ItemSelection.BOUNDS, selected);
}
}
})
Expand Down
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@

/*#*/ include('util/Numerical.js');
/*#*/ include('item/ChangeFlag.js');
/*#*/ include('item/ItemSelection.js');
/*#*/ include('path/SegmentSelection.js');
106 changes: 59 additions & 47 deletions src/item/Item.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,22 +51,30 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
_applyMatrix: true,
_canApplyMatrix: true,
_canScaleStroke: false,
_pivot: null,
_visible: true,
_blendMode: 'normal',
_opacity: 1,
_locked: false,
_guide: false,
_clipMask: false,
_selection: 0,
_boundsSelected: false,
_selectChildren: false,
// Provide information about fields to be serialized, with their defaults
// that can be ommited.
// that can be omitted.
_serializeFields: {
name: null,
applyMatrix: null,
matrix: new Matrix(),
pivot: null,
locked: false,
visible: true,
blendMode: 'normal',
opacity: 1,
locked: false,
guide: false,
selected: false,
clipMask: false,
selected: false,
data: {}
}
},
Expand Down Expand Up @@ -425,7 +433,6 @@ new function() { // Injection scope for various item event handlers
* @default false
* @ignore
*/
_locked: false,

/**
* Specifies whether the item is visible. When set to `false`, the item
Expand All @@ -446,7 +453,6 @@ new function() { // Injection scope for various item event handlers
* // Hide the path:
* path.visible = false;
*/
_visible: true,

/**
* The blend mode with which the item is composited onto the canvas. Both
Expand Down Expand Up @@ -488,7 +494,6 @@ new function() { // Injection scope for various item event handlers
* // Set the blend mode of circle2:
* circle2.blendMode = 'multiply';
*/
_blendMode: 'normal',

/**
* The opacity of the item as a value between `0` and `1`.
Expand Down Expand Up @@ -516,7 +521,6 @@ new function() { // Injection scope for various item event handlers
* // Make circle2 50% transparent:
* circle2.opacity = 0.5;
*/
_opacity: 1,

// TODO: Implement guides
/**
Expand All @@ -528,7 +532,26 @@ new function() { // Injection scope for various item event handlers
* @default true
* @ignore
*/
_guide: false,

getSelection: function() {
return this._selection;
},

setSelection: function(selection) {
if (selection !== this._selection) {
this._selection = selection;
var project = this._project;
if (project) {
project._updateSelection(this);
this._changed(/*#=*/Change.ATTRIBUTE);
}
}
},

changeSelection: function(flag, selected) {
var selection = this._selection;
this.setSelection(selected ? selection | flag : selection & ~flag);
},

/**
* Specifies whether the item is selected. This will also return `true` for
Expand Down Expand Up @@ -563,39 +586,29 @@ new function() { // Injection scope for various item event handlers
if (children[i].isSelected())
return true;
}
return this._selected;
return !!(this._selection & /*#=*/ItemSelection.ITEM);
},

setSelected: function(selected, noChildren) {
// Don't recursively call #setSelected() if it was called with
// noChildren set to true, see #setFullySelected().
if (!noChildren && this._selectChildren) {
setSelected: function(selected) {
if (this._selectChildren) {
var children = this._children;
for (var i = 0, l = children.length; i < l; i++)
children[i].setSelected(selected);
}
if ((selected = !!selected) ^ this._selected) {
this._selected = selected;
var project = this._project;
if (project) {
project._updateSelection(this);
this._changed(/*#=*/Change.ATTRIBUTE);
}
}
this.changeSelection(/*#=*/ItemSelection.ITEM, selected);
},

_selected: false,

isFullySelected: function() {
var children = this._children;
if (children && this._selected) {
var children = this._children,
selected = !!(this._selection & /*#=*/ItemSelection.ITEM);
if (children && selected) {
for (var i = 0, l = children.length; i < l; i++)
if (!children[i].isFullySelected())
return false;
return true;
}
// If there are no children, this is the same as #selected
return this._selected;
return selected;
},

setFullySelected: function(selected) {
Expand All @@ -604,8 +617,7 @@ new function() { // Injection scope for various item event handlers
for (var i = 0, l = children.length; i < l; i++)
children[i].setFullySelected(selected);
}
// Pass true for hidden noChildren argument
this.setSelected(selected, true);
this.changeSelection(/*#=*/ItemSelection.ITEM, selected);
},

/**
Expand Down Expand Up @@ -636,8 +648,6 @@ new function() { // Injection scope for various item event handlers
}
},

_clipMask: false,

// TODO: get/setIsolated (print specific feature)
// TODO: get/setKnockout (print specific feature)
// TODO: get/setAlphaIsShape
Expand Down Expand Up @@ -778,9 +788,7 @@ new function() { // Injection scope for various item event handlers
this._pivot = Point.read(arguments, 0, { clone: true, readNull: true });
// No need for _changed() since the only thing this affects is _position
this._position = undefined;
},

_pivot: null,
}
}, Base.each({ // Produce getters for bounds properties:
getStrokeBounds: { stroke: true },
getHandleBounds: { handle: true },
Expand Down Expand Up @@ -1565,9 +1573,9 @@ new function() { // Injection scope for various item event handlers
// in case #applyMatrix is true.
this.setApplyMatrix(source._applyMatrix);
this.setPivot(source._pivot);
// Copy over the selection state, use setSelected so the item
// is also added to Project#selectedItems if it is selected.
this.setSelected(source._selected);
// Copy over the selection state, use setSelection so the item
// is also added to Project#_selectionItems if it is selected.
this.setSelection(source._selection);
// Copy over data and name as well.
var data = source._data,
name = source._name;
Expand Down Expand Up @@ -1876,7 +1884,7 @@ new function() { // Injection scope for hit-test functions shared with project
// See if we should check self (own content), by filtering for type,
// guides and selected items if that's required.
var checkSelf = !(options.guides && !this._guide
|| options.selected && !this._selected
|| options.selected && !this.isSelected(true)
// Support legacy Item#type property to match hyphenated
// class-names.
|| options.type && options.type !== Base.hyphenate(this._class)
Expand Down Expand Up @@ -4240,23 +4248,27 @@ new function() { // Injection scope for hit-test functions shared with project
return updated;
},

_drawSelection: function(ctx, matrix, size, selectedItems, updateVersion) {
if ((this._drawSelected || this._boundsSelected)
&& this._isUpdated(updateVersion)) {
_drawSelection: function(ctx, matrix, size, selectionItems, updateVersion) {
var selection = this._selection,
itemSelected = selection & /*#=*/ItemSelection.ITEM,
boundsSelected = selection & /*#=*/ItemSelection.BOUNDS
|| itemSelected && this._boundsSelected;
if (!this._drawSelected)
itemSelected = false;
if ((itemSelected || boundsSelected) && this._isUpdated(updateVersion)) {
// Allow definition of selected color on a per item and per
// layer level, with a fallback to #009dec
var layer,
color = this.getSelectedColor(true)
|| (layer = this.getLayer()) && layer.getSelectedColor(true),
color = this.getSelectedColor(true) || (layer = this.getLayer())
&& layer.getSelectedColor(true),
mx = matrix.appended(this.getGlobalMatrix(true));
ctx.strokeStyle = ctx.fillStyle = color
? color.toCanvasStyle(ctx) : '#009dec';
if (this._drawSelected)
this._drawSelected(ctx, mx, selectedItems);
if (this._boundsSelected) {
if (itemSelected)
this._drawSelected(ctx, mx, selectionItems);
if (boundsSelected) {
var half = size / 2,
coords = mx._transformCorners(
this.getInternalBounds());
coords = mx._transformCorners(this.getInternalBounds());
// Now draw a rectangle that connects the transformed
// bounds corners, and draw the corners.
ctx.beginPath();
Expand Down
17 changes: 17 additions & 0 deletions src/item/ItemSelection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
* http://paperjs.org/
*
* Copyright (c) 2011 - 2016, Juerg Lehni & Jonathan Puckey
* http://scratchdisk.com/ & http://jonathanpuckey.com/
*
* Distributed under the MIT license. See LICENSE file for details.
*
* All rights reserved.
*/

var ItemSelection = {
ITEM: 1,
BOUNDS: 2,
PIVOT: 4
};
44 changes: 22 additions & 22 deletions src/item/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
// (e.g. PointText#_getBounds)
this._view = View.create(this,
element || CanvasProvider.getCanvas(1, 1));
this._selectedItems = {};
this._selectedItemCount = 0;
this._selectionItems = {};
this._selectionCount = 0;
// See Item#draw() for an explanation of _updateVersion
this._updateVersion = 0;
// Change tracking, not in use for now. Activate once required:
Expand Down Expand Up @@ -282,15 +282,15 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
// TODO: Return groups if their children are all selected, and filter
// out their children from the list.
// TODO: The order of these items should be that of their drawing order.
var selectedItems = this._selectedItems,
var selectionItems = this._selectionItems,
items = [];
for (var id in selectedItems) {
var item = selectedItems[id];
if (item.isInserted()) {
for (var id in selectionItems) {
var item = selectionItems[id],
selection = item._selection;
if (selection & /*#=*/ItemSelection.ITEM && item.isInserted()) {
items.push(item);
} else {
this._selectedItemCount--;
delete selectedItems[id];
} else if (!selection) {
this._updateSelection(item);
}
}
return items;
Expand All @@ -299,15 +299,15 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{

_updateSelection: function(item) {
var id = item._id,
selectedItems = this._selectedItems;
if (item._selected) {
if (selectedItems[id] !== item) {
this._selectedItemCount++;
selectedItems[id] = item;
selectionItems = this._selectionItems;
if (item._selection) {
if (selectionItems[id] !== item) {
this._selectionCount++;
selectionItems[id] = item;
}
} else if (selectedItems[id] === item) {
this._selectedItemCount--;
delete selectedItems[id];
} else if (selectionItems[id] === item) {
this._selectionCount--;
delete selectionItems[id];
}
},

Expand All @@ -324,9 +324,9 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
* Deselects all selected items in the project.
*/
deselectAll: function() {
var selectedItems = this._selectedItems;
for (var i in selectedItems)
selectedItems[i].setFullySelected(false);
var selectionItems = this._selectionItems;
for (var i in selectionItems)
selectionItems[i].setFullySelected(false);
},

/**
Expand Down Expand Up @@ -866,10 +866,10 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
ctx.restore();

// Draw the selection of the selected items in the project:
if (this._selectedItemCount > 0) {
if (this._selectionCount > 0) {
ctx.save();
ctx.strokeWidth = 1;
var items = this._selectedItems,
var items = this._selectionItems,
size = this._scope.settings.handleSize,
version = this._updateVersion;
for (var id in items) {
Expand Down
4 changes: 2 additions & 2 deletions src/path/CompoundPath.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,14 +293,14 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
}
},

_drawSelected: function(ctx, matrix, selectedItems) {
_drawSelected: function(ctx, matrix, selectionItems) {
var children = this._children;
for (var i = 0, l = children.length; i < l; i++) {
var child = children[i],
mx = child._matrix;
// Do not draw this child now if it's separately marked as selected,
// as it would be drawn twice otherwise.
if (!selectedItems[child._id]) {
if (!selectionItems[child._id]) {
child._drawSelected(ctx, mx.isIdentity() ? matrix
: matrix.appended(mx));
}
Expand Down
Loading

0 comments on commit f0edcd3

Please sign in to comment.