mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-07 13:22:07 -05:00
Fix #769: Implement Item#selection flags to separate selection from item and bounds.
This commit is contained in:
parent
bb19fade56
commit
f0edcd31b0
10 changed files with 170 additions and 146 deletions
|
@ -904,18 +904,13 @@ new function() {
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
isSelected: function() {
|
isSelected: function() {
|
||||||
return this._owner._boundsSelected;
|
return !!(this._owner._selection & /*#=*/ItemSelection.BOUNDS);
|
||||||
},
|
},
|
||||||
|
|
||||||
setSelected: function(selected) {
|
setSelected: function(selected) {
|
||||||
var owner = this._owner;
|
var owner = this._owner;
|
||||||
if (owner.setSelected) {
|
if (owner.changeSelection) {
|
||||||
owner._boundsSelected = selected;
|
owner.changeSelection(/*#=*/ItemSelection.BOUNDS, 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,4 +12,5 @@
|
||||||
|
|
||||||
/*#*/ include('util/Numerical.js');
|
/*#*/ include('util/Numerical.js');
|
||||||
/*#*/ include('item/ChangeFlag.js');
|
/*#*/ include('item/ChangeFlag.js');
|
||||||
|
/*#*/ include('item/ItemSelection.js');
|
||||||
/*#*/ include('path/SegmentSelection.js');
|
/*#*/ include('path/SegmentSelection.js');
|
||||||
|
|
106
src/item/Item.js
106
src/item/Item.js
|
@ -51,22 +51,30 @@ var Item = Base.extend(Emitter, /** @lends Item# */{
|
||||||
_applyMatrix: true,
|
_applyMatrix: true,
|
||||||
_canApplyMatrix: true,
|
_canApplyMatrix: true,
|
||||||
_canScaleStroke: false,
|
_canScaleStroke: false,
|
||||||
|
_pivot: null,
|
||||||
|
_visible: true,
|
||||||
|
_blendMode: 'normal',
|
||||||
|
_opacity: 1,
|
||||||
|
_locked: false,
|
||||||
|
_guide: false,
|
||||||
|
_clipMask: false,
|
||||||
|
_selection: 0,
|
||||||
_boundsSelected: false,
|
_boundsSelected: false,
|
||||||
_selectChildren: false,
|
_selectChildren: false,
|
||||||
// Provide information about fields to be serialized, with their defaults
|
// Provide information about fields to be serialized, with their defaults
|
||||||
// that can be ommited.
|
// that can be omitted.
|
||||||
_serializeFields: {
|
_serializeFields: {
|
||||||
name: null,
|
name: null,
|
||||||
applyMatrix: null,
|
applyMatrix: null,
|
||||||
matrix: new Matrix(),
|
matrix: new Matrix(),
|
||||||
pivot: null,
|
pivot: null,
|
||||||
locked: false,
|
|
||||||
visible: true,
|
visible: true,
|
||||||
blendMode: 'normal',
|
blendMode: 'normal',
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
|
locked: false,
|
||||||
guide: false,
|
guide: false,
|
||||||
selected: false,
|
|
||||||
clipMask: false,
|
clipMask: false,
|
||||||
|
selected: false,
|
||||||
data: {}
|
data: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -425,7 +433,6 @@ new function() { // Injection scope for various item event handlers
|
||||||
* @default false
|
* @default false
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
_locked: false,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies whether the item is visible. When set to `false`, the item
|
* Specifies whether the item is visible. When set to `false`, the item
|
||||||
|
@ -446,7 +453,6 @@ new function() { // Injection scope for various item event handlers
|
||||||
* // Hide the path:
|
* // Hide the path:
|
||||||
* path.visible = false;
|
* path.visible = false;
|
||||||
*/
|
*/
|
||||||
_visible: true,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The blend mode with which the item is composited onto the canvas. Both
|
* The blend mode with which the item is composited onto the canvas. Both
|
||||||
|
@ -488,7 +494,6 @@ new function() { // Injection scope for various item event handlers
|
||||||
* // Set the blend mode of circle2:
|
* // Set the blend mode of circle2:
|
||||||
* circle2.blendMode = 'multiply';
|
* circle2.blendMode = 'multiply';
|
||||||
*/
|
*/
|
||||||
_blendMode: 'normal',
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The opacity of the item as a value between `0` and `1`.
|
* The opacity of the item as a value between `0` and `1`.
|
||||||
|
@ -516,7 +521,6 @@ new function() { // Injection scope for various item event handlers
|
||||||
* // Make circle2 50% transparent:
|
* // Make circle2 50% transparent:
|
||||||
* circle2.opacity = 0.5;
|
* circle2.opacity = 0.5;
|
||||||
*/
|
*/
|
||||||
_opacity: 1,
|
|
||||||
|
|
||||||
// TODO: Implement guides
|
// TODO: Implement guides
|
||||||
/**
|
/**
|
||||||
|
@ -528,7 +532,26 @@ new function() { // Injection scope for various item event handlers
|
||||||
* @default true
|
* @default true
|
||||||
* @ignore
|
* @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
|
* Specifies whether the item is selected. This will also return `true` for
|
||||||
|
@ -563,39 +586,29 @@ new function() { // Injection scope for various item event handlers
|
||||||
if (children[i].isSelected())
|
if (children[i].isSelected())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return this._selected;
|
return !!(this._selection & /*#=*/ItemSelection.ITEM);
|
||||||
},
|
},
|
||||||
|
|
||||||
setSelected: function(selected, noChildren) {
|
setSelected: function(selected) {
|
||||||
// Don't recursively call #setSelected() if it was called with
|
if (this._selectChildren) {
|
||||||
// noChildren set to true, see #setFullySelected().
|
|
||||||
if (!noChildren && this._selectChildren) {
|
|
||||||
var children = this._children;
|
var children = this._children;
|
||||||
for (var i = 0, l = children.length; i < l; i++)
|
for (var i = 0, l = children.length; i < l; i++)
|
||||||
children[i].setSelected(selected);
|
children[i].setSelected(selected);
|
||||||
}
|
}
|
||||||
if ((selected = !!selected) ^ this._selected) {
|
this.changeSelection(/*#=*/ItemSelection.ITEM, selected);
|
||||||
this._selected = selected;
|
|
||||||
var project = this._project;
|
|
||||||
if (project) {
|
|
||||||
project._updateSelection(this);
|
|
||||||
this._changed(/*#=*/Change.ATTRIBUTE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_selected: false,
|
|
||||||
|
|
||||||
isFullySelected: function() {
|
isFullySelected: function() {
|
||||||
var children = this._children;
|
var children = this._children,
|
||||||
if (children && this._selected) {
|
selected = !!(this._selection & /*#=*/ItemSelection.ITEM);
|
||||||
|
if (children && selected) {
|
||||||
for (var i = 0, l = children.length; i < l; i++)
|
for (var i = 0, l = children.length; i < l; i++)
|
||||||
if (!children[i].isFullySelected())
|
if (!children[i].isFullySelected())
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// If there are no children, this is the same as #selected
|
// If there are no children, this is the same as #selected
|
||||||
return this._selected;
|
return selected;
|
||||||
},
|
},
|
||||||
|
|
||||||
setFullySelected: function(selected) {
|
setFullySelected: function(selected) {
|
||||||
|
@ -604,8 +617,7 @@ new function() { // Injection scope for various item event handlers
|
||||||
for (var i = 0, l = children.length; i < l; i++)
|
for (var i = 0, l = children.length; i < l; i++)
|
||||||
children[i].setFullySelected(selected);
|
children[i].setFullySelected(selected);
|
||||||
}
|
}
|
||||||
// Pass true for hidden noChildren argument
|
this.changeSelection(/*#=*/ItemSelection.ITEM, selected);
|
||||||
this.setSelected(selected, true);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -636,8 +648,6 @@ new function() { // Injection scope for various item event handlers
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_clipMask: false,
|
|
||||||
|
|
||||||
// TODO: get/setIsolated (print specific feature)
|
// TODO: get/setIsolated (print specific feature)
|
||||||
// TODO: get/setKnockout (print specific feature)
|
// TODO: get/setKnockout (print specific feature)
|
||||||
// TODO: get/setAlphaIsShape
|
// TODO: get/setAlphaIsShape
|
||||||
|
@ -778,9 +788,7 @@ new function() { // Injection scope for various item event handlers
|
||||||
this._pivot = Point.read(arguments, 0, { clone: true, readNull: true });
|
this._pivot = Point.read(arguments, 0, { clone: true, readNull: true });
|
||||||
// No need for _changed() since the only thing this affects is _position
|
// No need for _changed() since the only thing this affects is _position
|
||||||
this._position = undefined;
|
this._position = undefined;
|
||||||
},
|
}
|
||||||
|
|
||||||
_pivot: null,
|
|
||||||
}, Base.each({ // Produce getters for bounds properties:
|
}, Base.each({ // Produce getters for bounds properties:
|
||||||
getStrokeBounds: { stroke: true },
|
getStrokeBounds: { stroke: true },
|
||||||
getHandleBounds: { handle: true },
|
getHandleBounds: { handle: true },
|
||||||
|
@ -1565,9 +1573,9 @@ new function() { // Injection scope for various item event handlers
|
||||||
// in case #applyMatrix is true.
|
// in case #applyMatrix is true.
|
||||||
this.setApplyMatrix(source._applyMatrix);
|
this.setApplyMatrix(source._applyMatrix);
|
||||||
this.setPivot(source._pivot);
|
this.setPivot(source._pivot);
|
||||||
// Copy over the selection state, use setSelected so the item
|
// Copy over the selection state, use setSelection so the item
|
||||||
// is also added to Project#selectedItems if it is selected.
|
// is also added to Project#_selectionItems if it is selected.
|
||||||
this.setSelected(source._selected);
|
this.setSelection(source._selection);
|
||||||
// Copy over data and name as well.
|
// Copy over data and name as well.
|
||||||
var data = source._data,
|
var data = source._data,
|
||||||
name = source._name;
|
name = source._name;
|
||||||
|
@ -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,
|
// See if we should check self (own content), by filtering for type,
|
||||||
// guides and selected items if that's required.
|
// guides and selected items if that's required.
|
||||||
var checkSelf = !(options.guides && !this._guide
|
var checkSelf = !(options.guides && !this._guide
|
||||||
|| options.selected && !this._selected
|
|| options.selected && !this.isSelected(true)
|
||||||
// Support legacy Item#type property to match hyphenated
|
// Support legacy Item#type property to match hyphenated
|
||||||
// class-names.
|
// class-names.
|
||||||
|| options.type && options.type !== Base.hyphenate(this._class)
|
|| options.type && options.type !== Base.hyphenate(this._class)
|
||||||
|
@ -4240,23 +4248,27 @@ new function() { // Injection scope for hit-test functions shared with project
|
||||||
return updated;
|
return updated;
|
||||||
},
|
},
|
||||||
|
|
||||||
_drawSelection: function(ctx, matrix, size, selectedItems, updateVersion) {
|
_drawSelection: function(ctx, matrix, size, selectionItems, updateVersion) {
|
||||||
if ((this._drawSelected || this._boundsSelected)
|
var selection = this._selection,
|
||||||
&& this._isUpdated(updateVersion)) {
|
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
|
// Allow definition of selected color on a per item and per
|
||||||
// layer level, with a fallback to #009dec
|
// layer level, with a fallback to #009dec
|
||||||
var layer,
|
var layer,
|
||||||
color = this.getSelectedColor(true)
|
color = this.getSelectedColor(true) || (layer = this.getLayer())
|
||||||
|| (layer = this.getLayer()) && layer.getSelectedColor(true),
|
&& layer.getSelectedColor(true),
|
||||||
mx = matrix.appended(this.getGlobalMatrix(true));
|
mx = matrix.appended(this.getGlobalMatrix(true));
|
||||||
ctx.strokeStyle = ctx.fillStyle = color
|
ctx.strokeStyle = ctx.fillStyle = color
|
||||||
? color.toCanvasStyle(ctx) : '#009dec';
|
? color.toCanvasStyle(ctx) : '#009dec';
|
||||||
if (this._drawSelected)
|
if (itemSelected)
|
||||||
this._drawSelected(ctx, mx, selectedItems);
|
this._drawSelected(ctx, mx, selectionItems);
|
||||||
if (this._boundsSelected) {
|
if (boundsSelected) {
|
||||||
var half = size / 2,
|
var half = size / 2,
|
||||||
coords = mx._transformCorners(
|
coords = mx._transformCorners(this.getInternalBounds());
|
||||||
this.getInternalBounds());
|
|
||||||
// Now draw a rectangle that connects the transformed
|
// Now draw a rectangle that connects the transformed
|
||||||
// bounds corners, and draw the corners.
|
// bounds corners, and draw the corners.
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
|
|
17
src/item/ItemSelection.js
Normal file
17
src/item/ItemSelection.js
Normal file
|
@ -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
|
||||||
|
};
|
|
@ -62,8 +62,8 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
|
||||||
// (e.g. PointText#_getBounds)
|
// (e.g. PointText#_getBounds)
|
||||||
this._view = View.create(this,
|
this._view = View.create(this,
|
||||||
element || CanvasProvider.getCanvas(1, 1));
|
element || CanvasProvider.getCanvas(1, 1));
|
||||||
this._selectedItems = {};
|
this._selectionItems = {};
|
||||||
this._selectedItemCount = 0;
|
this._selectionCount = 0;
|
||||||
// See Item#draw() for an explanation of _updateVersion
|
// See Item#draw() for an explanation of _updateVersion
|
||||||
this._updateVersion = 0;
|
this._updateVersion = 0;
|
||||||
// Change tracking, not in use for now. Activate once required:
|
// Change tracking, not in use for now. Activate once required:
|
||||||
|
@ -282,15 +282,15 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
|
||||||
// TODO: Return groups if their children are all selected, and filter
|
// TODO: Return groups if their children are all selected, and filter
|
||||||
// out their children from the list.
|
// out their children from the list.
|
||||||
// TODO: The order of these items should be that of their drawing order.
|
// TODO: The order of these items should be that of their drawing order.
|
||||||
var selectedItems = this._selectedItems,
|
var selectionItems = this._selectionItems,
|
||||||
items = [];
|
items = [];
|
||||||
for (var id in selectedItems) {
|
for (var id in selectionItems) {
|
||||||
var item = selectedItems[id];
|
var item = selectionItems[id],
|
||||||
if (item.isInserted()) {
|
selection = item._selection;
|
||||||
|
if (selection & /*#=*/ItemSelection.ITEM && item.isInserted()) {
|
||||||
items.push(item);
|
items.push(item);
|
||||||
} else {
|
} else if (!selection) {
|
||||||
this._selectedItemCount--;
|
this._updateSelection(item);
|
||||||
delete selectedItems[id];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
|
@ -299,15 +299,15 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
|
||||||
|
|
||||||
_updateSelection: function(item) {
|
_updateSelection: function(item) {
|
||||||
var id = item._id,
|
var id = item._id,
|
||||||
selectedItems = this._selectedItems;
|
selectionItems = this._selectionItems;
|
||||||
if (item._selected) {
|
if (item._selection) {
|
||||||
if (selectedItems[id] !== item) {
|
if (selectionItems[id] !== item) {
|
||||||
this._selectedItemCount++;
|
this._selectionCount++;
|
||||||
selectedItems[id] = item;
|
selectionItems[id] = item;
|
||||||
}
|
}
|
||||||
} else if (selectedItems[id] === item) {
|
} else if (selectionItems[id] === item) {
|
||||||
this._selectedItemCount--;
|
this._selectionCount--;
|
||||||
delete selectedItems[id];
|
delete selectionItems[id];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -324,9 +324,9 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
|
||||||
* Deselects all selected items in the project.
|
* Deselects all selected items in the project.
|
||||||
*/
|
*/
|
||||||
deselectAll: function() {
|
deselectAll: function() {
|
||||||
var selectedItems = this._selectedItems;
|
var selectionItems = this._selectionItems;
|
||||||
for (var i in selectedItems)
|
for (var i in selectionItems)
|
||||||
selectedItems[i].setFullySelected(false);
|
selectionItems[i].setFullySelected(false);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -866,10 +866,10 @@ var Project = PaperScopeItem.extend(/** @lends Project# */{
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
||||||
// Draw the selection of the selected items in the project:
|
// Draw the selection of the selected items in the project:
|
||||||
if (this._selectedItemCount > 0) {
|
if (this._selectionCount > 0) {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.strokeWidth = 1;
|
ctx.strokeWidth = 1;
|
||||||
var items = this._selectedItems,
|
var items = this._selectionItems,
|
||||||
size = this._scope.settings.handleSize,
|
size = this._scope.settings.handleSize,
|
||||||
version = this._updateVersion;
|
version = this._updateVersion;
|
||||||
for (var id in items) {
|
for (var id in items) {
|
||||||
|
|
|
@ -293,14 +293,14 @@ var CompoundPath = PathItem.extend(/** @lends CompoundPath# */{
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_drawSelected: function(ctx, matrix, selectedItems) {
|
_drawSelected: function(ctx, matrix, selectionItems) {
|
||||||
var children = this._children;
|
var children = this._children;
|
||||||
for (var i = 0, l = children.length; i < l; i++) {
|
for (var i = 0, l = children.length; i < l; i++) {
|
||||||
var child = children[i],
|
var child = children[i],
|
||||||
mx = child._matrix;
|
mx = child._matrix;
|
||||||
// Do not draw this child now if it's separately marked as selected,
|
// Do not draw this child now if it's separately marked as selected,
|
||||||
// as it would be drawn twice otherwise.
|
// as it would be drawn twice otherwise.
|
||||||
if (!selectedItems[child._id]) {
|
if (!selectionItems[child._id]) {
|
||||||
child._drawSelected(ctx, mx.isIdentity() ? matrix
|
child._drawSelected(ctx, mx.isIdentity() ? matrix
|
||||||
: matrix.appended(mx));
|
: matrix.appended(mx));
|
||||||
}
|
}
|
||||||
|
|
|
@ -935,8 +935,8 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
*/
|
*/
|
||||||
isFullySelected: function() {
|
isFullySelected: function() {
|
||||||
var length = this._segments.length;
|
var length = this._segments.length;
|
||||||
return this._selected && length > 0 && this._segmentSelection
|
return this.isSelected(true) && length > 0 && this._segmentSelection
|
||||||
=== length * /*#=*/SegmentSelection.SEGMENT;
|
=== length * /*#=*/SegmentSelection.ALL;
|
||||||
},
|
},
|
||||||
|
|
||||||
setFullySelected: function(selected) {
|
setFullySelected: function(selected) {
|
||||||
|
@ -947,22 +947,20 @@ var Path = PathItem.extend(/** @lends Path# */{
|
||||||
this.setSelected(selected);
|
this.setSelected(selected);
|
||||||
},
|
},
|
||||||
|
|
||||||
setSelected: function setSelected(selected) {
|
setSelection: function setSelection(selection) {
|
||||||
// Deselect all segments when path is marked as not selected
|
// Deselect all segments when path is marked as not selected
|
||||||
if (!selected)
|
if (!(selection & /*#=*/ItemSelection.ITEM))
|
||||||
this._selectSegments(false);
|
this._selectSegments(false);
|
||||||
// No need to pass true for noChildren since Path has none anyway.
|
setSelection.base.call(this, selection);
|
||||||
setSelected.base.call(this, selected);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_selectSegments: function(selected) {
|
_selectSegments: function(selected) {
|
||||||
var length = this._segments.length;
|
var segments = this._segments,
|
||||||
this._segmentSelection = selected
|
length = segments.length,
|
||||||
? length * /*#=*/SegmentSelection.SEGMENT : 0;
|
selection = selected ? /*#=*/SegmentSelection.ALL : 0;
|
||||||
for (var i = 0; i < length; i++) {
|
this._segmentSelection = selection * length;
|
||||||
this._segments[i]._selection = selected
|
for (var i = 0; i < length; i++)
|
||||||
? /*#=*/SegmentSelection.SEGMENT : 0;
|
segments[i]._selection = selection;
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_updateSelection: function(segment, oldSelection, newSelection) {
|
_updateSelection: function(segment, oldSelection, newSelection) {
|
||||||
|
|
|
@ -115,7 +115,8 @@ var Segment = Base.extend(/** @lends Segment# */{
|
||||||
*/
|
*/
|
||||||
initialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) {
|
initialize: function Segment(arg0, arg1, arg2, arg3, arg4, arg5) {
|
||||||
var count = arguments.length,
|
var count = arguments.length,
|
||||||
point, handleIn, handleOut;
|
point, handleIn, handleOut,
|
||||||
|
selection;
|
||||||
// TODO: Use Point.read or Point.readNamed to read these?
|
// TODO: Use Point.read or Point.readNamed to read these?
|
||||||
if (count === 0) {
|
if (count === 0) {
|
||||||
// Nothing
|
// Nothing
|
||||||
|
@ -125,17 +126,17 @@ var Segment = Base.extend(/** @lends Segment# */{
|
||||||
point = arg0.point;
|
point = arg0.point;
|
||||||
handleIn = arg0.handleIn;
|
handleIn = arg0.handleIn;
|
||||||
handleOut = arg0.handleOut;
|
handleOut = arg0.handleOut;
|
||||||
|
selection = arg0.selection;
|
||||||
} else {
|
} else {
|
||||||
point = arg0;
|
point = arg0;
|
||||||
}
|
}
|
||||||
} else if (count === 2 && typeof arg0 === 'number') {
|
} else if (typeof arg0 === 'object') {
|
||||||
point = arguments;
|
// It doesn't matter if all of these arguments exist.
|
||||||
} else if (count <= 3) {
|
// new SegmentPoint() produces creates points with (0, 0) otherwise.
|
||||||
point = arg0;
|
point = arg0;
|
||||||
// Doesn't matter if these arguments exist, SegmentPointcreate
|
|
||||||
// produces creates points with (0, 0) otherwise
|
|
||||||
handleIn = arg1;
|
handleIn = arg1;
|
||||||
handleOut = arg2;
|
handleOut = arg2;
|
||||||
|
selection = arg3;
|
||||||
} else { // Read points from the arguments list as a row of numbers
|
} else { // Read points from the arguments list as a row of numbers
|
||||||
point = arg0 !== undefined ? [ arg0, arg1 ] : null;
|
point = arg0 !== undefined ? [ arg0, arg1 ] : null;
|
||||||
handleIn = arg2 !== undefined ? [ arg2, arg3 ] : null;
|
handleIn = arg2 !== undefined ? [ arg2, arg3 ] : null;
|
||||||
|
@ -144,14 +145,20 @@ var Segment = Base.extend(/** @lends Segment# */{
|
||||||
new SegmentPoint(point, this, '_point');
|
new SegmentPoint(point, this, '_point');
|
||||||
new SegmentPoint(handleIn, this, '_handleIn');
|
new SegmentPoint(handleIn, this, '_handleIn');
|
||||||
new SegmentPoint(handleOut, this, '_handleOut');
|
new SegmentPoint(handleOut, this, '_handleOut');
|
||||||
|
if (selection)
|
||||||
|
this.setSelection(selection);
|
||||||
},
|
},
|
||||||
|
|
||||||
_serialize: function(options) {
|
_serialize: function(options) {
|
||||||
// If it is has no handles, only serialize point, otherwise handles too.
|
// If it is has no handles, only serialize point, otherwise handles too.
|
||||||
return Base.serialize(this.hasHandles()
|
var point = this._point,
|
||||||
? [this._point, this._handleIn, this._handleOut]
|
selection = this._selection,
|
||||||
: this._point,
|
obj = selection || this.hasHandles()
|
||||||
options, true);
|
? [point, this._handleIn, this._handleOut]
|
||||||
|
: point;
|
||||||
|
if (selection)
|
||||||
|
obj.push(selection);
|
||||||
|
return Base.serialize(obj, options, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
_changed: function(point) {
|
_changed: function(point) {
|
||||||
|
@ -253,8 +260,30 @@ var Segment = Base.extend(/** @lends Segment# */{
|
||||||
this._handleOut.set(0, 0);
|
this._handleOut.set(0, 0);
|
||||||
},
|
},
|
||||||
|
|
||||||
_getSelectionFlag: function(point) {
|
getSelection: function() {
|
||||||
return !point ? /*#=*/SegmentSelection.SEGMENT
|
return this._selection;
|
||||||
|
},
|
||||||
|
|
||||||
|
setSelection: function(selection) {
|
||||||
|
var oldSelection = this._selection,
|
||||||
|
path = this._path;
|
||||||
|
// Set the selection state even if path is not defined yet, to allow
|
||||||
|
// selected segments to be inserted into paths and make JSON
|
||||||
|
// deserialization work.
|
||||||
|
this._selection = selection = selection || 0;
|
||||||
|
// If the selection state of the segment has changed, we need to let
|
||||||
|
// it's path know and possibly add or remove it from
|
||||||
|
// project._selectionItems
|
||||||
|
if (path && selection !== oldSelection) {
|
||||||
|
path._updateSelection(this, oldSelection, selection);
|
||||||
|
// Let path know that we changed something and the view should be
|
||||||
|
// redrawn
|
||||||
|
path._changed(/*#=*/Change.ATTRIBUTE);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_getSelection: function(point) {
|
||||||
|
return !point ? /*#=*/SegmentSelection.ALL
|
||||||
: point === this._point ? /*#=*/SegmentSelection.POINT
|
: point === this._point ? /*#=*/SegmentSelection.POINT
|
||||||
: point === this._handleIn ? /*#=*/SegmentSelection.HANDLE_IN
|
: point === this._handleIn ? /*#=*/SegmentSelection.HANDLE_IN
|
||||||
: point === this._handleOut ? /*#=*/SegmentSelection.HANDLE_OUT
|
: point === this._handleOut ? /*#=*/SegmentSelection.HANDLE_OUT
|
||||||
|
@ -277,33 +306,13 @@ var Segment = Base.extend(/** @lends Segment# */{
|
||||||
* path.segments[2].selected = true;
|
* path.segments[2].selected = true;
|
||||||
*/
|
*/
|
||||||
isSelected: function(_point) {
|
isSelected: function(_point) {
|
||||||
return !!(this._selection & this._getSelectionFlag(_point));
|
return !!(this._selection & this._getSelection(_point));
|
||||||
},
|
},
|
||||||
|
|
||||||
setSelected: function(selected, _point) {
|
setSelected: function(selected, _point) {
|
||||||
var path = this._path,
|
var selection = this._selection,
|
||||||
selected = !!selected, // convert to boolean
|
flag = this._getSelection(_point);
|
||||||
selection = this._selection,
|
this.setSelection(selected ? selection | flag : selection & ~flag);
|
||||||
oldSelection = selection,
|
|
||||||
flag = this._getSelectionFlag(_point);
|
|
||||||
if (selected) {
|
|
||||||
selection |= flag;
|
|
||||||
} else {
|
|
||||||
selection &= ~flag;
|
|
||||||
}
|
|
||||||
// Set the selection state even if path is not defined yet, to allow
|
|
||||||
// selected segments to be inserted into paths and make JSON
|
|
||||||
// deserialization work.
|
|
||||||
this._selection = selection;
|
|
||||||
// If the selection state of the segment has changed, we need to let
|
|
||||||
// it's path know and possibly add or remove it from
|
|
||||||
// project._selectedItems
|
|
||||||
if (path && selection !== oldSelection) {
|
|
||||||
path._updateSelection(this, oldSelection, selection);
|
|
||||||
// Let path know that we changed something and the view should be
|
|
||||||
// redrawn
|
|
||||||
path._changed(/*#=*/Change.ATTRIBUTE);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,13 +18,14 @@
|
||||||
*/
|
*/
|
||||||
var SegmentPoint = Point.extend({
|
var SegmentPoint = Point.extend({
|
||||||
initialize: function SegmentPoint(point, owner, key) {
|
initialize: function SegmentPoint(point, owner, key) {
|
||||||
var x, y, selected;
|
var x, y,
|
||||||
|
selected;
|
||||||
if (!point) {
|
if (!point) {
|
||||||
x = y = 0;
|
x = y = 0;
|
||||||
} else if ((x = point[0]) !== undefined) { // Array-like
|
} else if ((x = point[0]) !== undefined) { // Array-like
|
||||||
y = point[1];
|
y = point[1];
|
||||||
} else {
|
} else {
|
||||||
// So we don't have to modify the point argument which causes
|
// So we don't have to modify the point argument which would cause
|
||||||
// deoptimization:
|
// deoptimization:
|
||||||
var pt = point;
|
var pt = point;
|
||||||
// If not Point-like already, read Point from arguments
|
// If not Point-like already, read Point from arguments
|
||||||
|
@ -38,9 +39,9 @@ var SegmentPoint = Point.extend({
|
||||||
this._x = x;
|
this._x = x;
|
||||||
this._y = y;
|
this._y = y;
|
||||||
this._owner = owner;
|
this._owner = owner;
|
||||||
// We have to set the owner's property that points to this point already
|
|
||||||
// now, so #setSelected(true) can work.
|
|
||||||
owner[key] = this;
|
owner[key] = this;
|
||||||
|
// We need to call #setSelected(true) after setting property on the
|
||||||
|
// owner that references this point.
|
||||||
if (selected)
|
if (selected)
|
||||||
this.setSelected(true);
|
this.setSelected(true);
|
||||||
},
|
},
|
||||||
|
@ -52,15 +53,6 @@ var SegmentPoint = Point.extend({
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
_serialize: function(options) {
|
|
||||||
var f = options.formatter,
|
|
||||||
x = f.number(this._x),
|
|
||||||
y = f.number(this._y);
|
|
||||||
return this.isSelected()
|
|
||||||
? { x: x, y: y, selected: true }
|
|
||||||
: [x, y];
|
|
||||||
},
|
|
||||||
|
|
||||||
getX: function() {
|
getX: function() {
|
||||||
return this._x;
|
return this._x;
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,11 +11,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Path#_segmentSelection is the addition of all segment's states, and is
|
// Path#_segmentSelection is the addition of all segment's states, and is
|
||||||
// compared with SegmentSelection.SEGMENT, the combination of all
|
// compared with SegmentSelection.ALL, the combination of all
|
||||||
// SegmentSelection values to see if all segments are fully selected.
|
// SegmentSelection values to see if all segments are fully selected.
|
||||||
var SegmentSelection = {
|
var SegmentSelection = {
|
||||||
HANDLE_IN: 1,
|
POINT: 1,
|
||||||
HANDLE_OUT: 2,
|
HANDLE_IN: 2,
|
||||||
POINT: 4,
|
HANDLE_OUT: 4,
|
||||||
SEGMENT: 7 // HANDLE_IN | HANDLE_OUT | POINT
|
ALL: 1 | 2 | 4 // POINT | HANDLE_IN | HANDLE_OUT
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue