mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-23 05:52:42 -05:00
Get move working
This commit is contained in:
parent
3126c2ca7f
commit
0e91439edd
8 changed files with 75 additions and 63 deletions
|
@ -4,4 +4,5 @@
|
||||||
margin: auto;
|
margin: auto;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
image-rendering: pixelated;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,12 +24,7 @@ class SelectMode extends React.Component {
|
||||||
bindAll(this, [
|
bindAll(this, [
|
||||||
'activateTool',
|
'activateTool',
|
||||||
'deactivateTool',
|
'deactivateTool',
|
||||||
'getHitOptions',
|
'getHitOptions'
|
||||||
'preProcessSelection',
|
|
||||||
'onMouseDown',
|
|
||||||
'onMouseMove',
|
|
||||||
'onMouseDrag',
|
|
||||||
'onMouseUp'
|
|
||||||
]);
|
]);
|
||||||
this._hitOptions = {
|
this._hitOptions = {
|
||||||
segments: true,
|
segments: true,
|
||||||
|
@ -71,48 +66,66 @@ class SelectMode extends React.Component {
|
||||||
selectRootItem();
|
selectRootItem();
|
||||||
this.tool = new paper.Tool();
|
this.tool = new paper.Tool();
|
||||||
|
|
||||||
|
const selectMode = this;
|
||||||
this.tool.onMouseDown = function (event) {
|
this.tool.onMouseDown = function (event) {
|
||||||
if (event.event.button > 0) return; // only first mouse button
|
if (event.event.button > 0) return; // only first mouse button
|
||||||
this.props.clearHoveredItem();
|
|
||||||
if (!this.boundingBoxTool.onMouseDown(
|
selectMode.props.clearHoveredItem();
|
||||||
event, event.modifiers.alt, event.modifiers.shift, true /* preselectedOnly */)) {
|
if (!selectMode.boundingBoxTool.onMouseDown(
|
||||||
this.selectionBoxMode = true;
|
event,
|
||||||
|
event.modifiers.alt,
|
||||||
|
event.modifiers.shift,
|
||||||
|
selectMode.getHitOptions(false /* preseelectedOnly */))) {
|
||||||
|
selectMode.selectionBoxMode = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.tool.onMouseMove = function (event) {
|
this.tool.onMouseMove = function (event) {
|
||||||
this.props.setHoveredItem(getHoveredItem(event, this.getHitOptions()));
|
const hoveredItem = getHoveredItem(event, selectMode.getHitOptions());
|
||||||
|
const oldHoveredItem = selectMode.props.hoveredItem;
|
||||||
|
if ((!hoveredItem && oldHoveredItem) || // There is no longer a hovered item
|
||||||
|
(hoveredItem && !oldHoveredItem) || // There is now a hovered item
|
||||||
|
(hoveredItem && oldHoveredItem && hoveredItem.id !== oldHoveredItem.id)) { // hovered item changed
|
||||||
|
if (oldHoveredItem) {
|
||||||
|
oldHoveredItem.remove();
|
||||||
|
}
|
||||||
|
selectMode.props.setHoveredItem(hoveredItem);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
this.tool.onMouseDrag = function (event) {
|
this.tool.onMouseDrag = function (event) {
|
||||||
if (event.event.button > 0) return; // only first mouse button
|
if (event.event.button > 0) return; // only first mouse button
|
||||||
if (this.selectionBoxMode) {
|
|
||||||
this.selectionRect = rectSelect(event);
|
if (selectMode.selectionBoxMode) {
|
||||||
|
selectMode.selectionRect = rectSelect(event);
|
||||||
// Remove this rect on the next drag and up event
|
// Remove this rect on the next drag and up event
|
||||||
this.selectionRect.removeOnDrag();
|
selectMode.selectionRect.removeOnDrag();
|
||||||
} else {
|
} else {
|
||||||
this.boundingBoxTool.onMouseDrag(event);
|
selectMode.boundingBoxTool.onMouseDrag(event);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
this.tool.onMouseUp = function (event) {
|
this.tool.onMouseUp = function (event) {
|
||||||
if (event.event.button > 0) return; // only first mouse button
|
if (event.event.button > 0) return; // only first mouse button
|
||||||
if (this.selectionBoxMode) {
|
|
||||||
processRectangularSelection(event, this.selectionRect);
|
if (selectMode.selectionBoxMode) {
|
||||||
this.selectionRect.remove();
|
if (selectMode.selectionRect) {
|
||||||
} else {
|
processRectangularSelection(event, selectMode.selectionRect);
|
||||||
this.boundingBoxTool.onMouseUp(event);
|
selectMode.selectionRect.remove();
|
||||||
this.props.onUpdateSvg();
|
|
||||||
}
|
}
|
||||||
this.selectionBoxMode = false;
|
selectMode.boundingBoxTool.setSelectionBounds();
|
||||||
this.selectionRect = null;
|
} else {
|
||||||
|
selectMode.boundingBoxTool.onMouseUp(event);
|
||||||
|
selectMode.props.onUpdateSvg();
|
||||||
|
}
|
||||||
|
selectMode.selectionBoxMode = false;
|
||||||
|
selectMode.selectionRect = null;
|
||||||
};
|
};
|
||||||
this.tool.activate();
|
this.tool.activate();
|
||||||
}
|
}
|
||||||
deactivateTool () {
|
deactivateTool () {
|
||||||
this.props.setHoveredItem();
|
this.props.clearHoveredItem();
|
||||||
this.tool.remove();
|
this.tool.remove();
|
||||||
this.tool = null;
|
this.tool = null;
|
||||||
this.hitResult = null;
|
this.hitResult = null;
|
||||||
|
@ -127,13 +140,15 @@ class SelectMode extends React.Component {
|
||||||
SelectMode.propTypes = {
|
SelectMode.propTypes = {
|
||||||
clearHoveredItem: PropTypes.func.isRequired,
|
clearHoveredItem: PropTypes.func.isRequired,
|
||||||
handleMouseDown: PropTypes.func.isRequired,
|
handleMouseDown: PropTypes.func.isRequired,
|
||||||
|
hoveredItem: PropTypes.instanceOf(paper.Item),
|
||||||
isSelectModeActive: PropTypes.bool.isRequired,
|
isSelectModeActive: PropTypes.bool.isRequired,
|
||||||
onUpdateSvg: PropTypes.func.isRequired,
|
onUpdateSvg: PropTypes.func.isRequired,
|
||||||
setHoveredItem: PropTypes.func.isRequired
|
setHoveredItem: PropTypes.func.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = state => ({
|
const mapStateToProps = state => ({
|
||||||
isSelectModeActive: state.scratchPaint.mode === Modes.SELECT
|
isSelectModeActive: state.scratchPaint.mode === Modes.SELECT,
|
||||||
|
hoveredItem: state.scratchPaint.hoveredItem
|
||||||
});
|
});
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
setHoveredItem: hoveredItem => {
|
setHoveredItem: hoveredItem => {
|
||||||
|
|
|
@ -40,17 +40,18 @@ class BoundingBoxTool {
|
||||||
* @param {!MouseEvent} event The mouse event
|
* @param {!MouseEvent} event The mouse event
|
||||||
* @param {boolean} clone Whether to clone on mouse down (e.g. alt key held)
|
* @param {boolean} clone Whether to clone on mouse down (e.g. alt key held)
|
||||||
* @param {boolean} multiselect Whether to multiselect on mouse down (e.g. shift key held)
|
* @param {boolean} multiselect Whether to multiselect on mouse down (e.g. shift key held)
|
||||||
* @param {boolean} preselectedOnly If true, only get hit results on items that are already selected
|
* @param {paper.hitOptions} hitOptions The options with which to detect whether mouse down has hit
|
||||||
|
* anything editable
|
||||||
* @return {boolean} True if there was a hit, false otherwise
|
* @return {boolean} True if there was a hit, false otherwise
|
||||||
*/
|
*/
|
||||||
onMouseDown (event, clone, multiselect, preselectedOnly) {
|
onMouseDown (event, clone, multiselect, hitOptions) {
|
||||||
const hitResults = paper.project.hitTestAll(event.point, this.getHitOptions(preselectedOnly));
|
const hitResults = paper.project.hitTestAll(event.point, hitOptions);
|
||||||
if (!hitResults || hitResults.length === 0) {
|
if (!hitResults || hitResults.length === 0) {
|
||||||
if (!multiselect) {
|
if (!multiselect) {
|
||||||
this.removeBoundsPath();
|
this.removeBoundsPath();
|
||||||
clearSelection();
|
clearSelection();
|
||||||
}
|
}
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer scale to trigger over rotate, and scale and rotate to trigger over other hits
|
// Prefer scale to trigger over rotate, and scale and rotate to trigger over other hits
|
||||||
|
@ -65,14 +66,17 @@ class BoundingBoxTool {
|
||||||
hitResult = hitResults[i];
|
hitResult = hitResults[i];
|
||||||
this.mode = Modes.ROTATE;
|
this.mode = Modes.ROTATE;
|
||||||
this._modeMap[this.mode].onMouseDown(hitResult, this.boundsPath, getSelectedItems());
|
this._modeMap[this.mode].onMouseDown(hitResult, this.boundsPath, getSelectedItems());
|
||||||
} else {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.mode) {
|
||||||
this.mode = Modes.MOVE;
|
this.mode = Modes.MOVE;
|
||||||
this._modeMap[this.mode].onMouseDown(hitResult, clone, multiselect);
|
this._modeMap[this.mode].onMouseDown(hitResult, clone, multiselect);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// while transforming object, never show the bounds stuff
|
// while transforming object, never show the bounds stuff
|
||||||
this.removeBoundsPath();
|
this.removeBoundsPath();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
onMouseDrag (event) {
|
onMouseDrag (event) {
|
||||||
if (event.event.button > 0) return; // only first mouse button
|
if (event.event.button > 0) return; // only first mouse button
|
||||||
|
@ -83,13 +87,8 @@ class BoundingBoxTool {
|
||||||
this._modeMap[this.mode].onMouseUp(event);
|
this._modeMap[this.mode].onMouseUp(event);
|
||||||
|
|
||||||
this.mode = null;
|
this.mode = null;
|
||||||
|
|
||||||
if (getSelectedItems().length <= 0) {
|
|
||||||
this.removeBoundsPath();
|
|
||||||
} else {
|
|
||||||
this.setSelectionBounds();
|
this.setSelectionBounds();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
setSelectionBounds () {
|
setSelectionBounds () {
|
||||||
this.removeBoundsPath();
|
this.removeBoundsPath();
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {isGroup} from '../group';
|
import {isGroup} from '../group';
|
||||||
import {isCompoundPathItem, getRootItem} from '../item';
|
import {isCompoundPathItem, getRootItem} from '../item';
|
||||||
import {snapDeltaToAngle} from '../math';
|
import {snapDeltaToAngle} from '../math';
|
||||||
import {clearSelection, cloneSelection, getSelectedItems, setItemSelection, setGroupSelection} from '../selection';
|
import {clearSelection, cloneSelection, getSelectedItems, setItemSelection} from '../selection';
|
||||||
|
|
||||||
class MoveTool {
|
class MoveTool {
|
||||||
constructor () {
|
constructor () {
|
||||||
|
@ -14,31 +14,22 @@ class MoveTool {
|
||||||
* @param {boolean} multiselect Whether to multiselect on mouse down (e.g. shift key held)
|
* @param {boolean} multiselect Whether to multiselect on mouse down (e.g. shift key held)
|
||||||
*/
|
*/
|
||||||
onMouseDown (hitResult, clone, multiselect) {
|
onMouseDown (hitResult, clone, multiselect) {
|
||||||
|
const root = getRootItem(hitResult.item);
|
||||||
|
const item = isCompoundPathItem(root) || isGroup(root) ? root : hitResult.item;
|
||||||
|
if (!item.selected) {
|
||||||
// deselect all by default if multiselect isn't on
|
// deselect all by default if multiselect isn't on
|
||||||
if (!multiselect) {
|
if (!multiselect) {
|
||||||
clearSelection();
|
clearSelection();
|
||||||
}
|
}
|
||||||
// also needs some special love for compound paths and groups,
|
setItemSelection(item, true);
|
||||||
// as their children are not marked as "selected"
|
|
||||||
// deselect a currently selected item if multiselect is on
|
|
||||||
const root = getRootItem(hitResult.item);
|
|
||||||
if (isCompoundPathItem(root) || isGroup(root)) {
|
|
||||||
if (!root.selected) {
|
|
||||||
setGroupSelection(root, true);
|
|
||||||
} else if (multiselect) {
|
} else if (multiselect) {
|
||||||
setGroupSelection(root, false);
|
setItemSelection(item, false);
|
||||||
}
|
|
||||||
} else if (multiselect && hitResult.item.selected) {
|
|
||||||
setItemSelection(hitResult.item, false);
|
|
||||||
} else {
|
|
||||||
setItemSelection(hitResult.item, true);
|
|
||||||
}
|
}
|
||||||
if (clone) cloneSelection();
|
if (clone) cloneSelection();
|
||||||
this.selectedItems = getSelectedItems();
|
this.selectedItems = getSelectedItems();
|
||||||
}
|
}
|
||||||
onMouseDrag (event) {
|
onMouseDrag (event) {
|
||||||
const dragVector = (event.point - event.downPoint);
|
const dragVector = event.point.subtract(event.downPoint);
|
||||||
|
|
||||||
for (const item of this.selectedItems) {
|
for (const item of this.selectedItems) {
|
||||||
// add the position of the item before the drag started
|
// add the position of the item before the drag started
|
||||||
// for later use in the snap calculation
|
// for later use in the snap calculation
|
||||||
|
@ -47,10 +38,9 @@ class MoveTool {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.modifiers.shift) {
|
if (event.modifiers.shift) {
|
||||||
item.position = item.data.origPos +
|
item.position = item.data.origPos.add(snapDeltaToAngle(dragVector, Math.PI / 4));
|
||||||
snapDeltaToAngle(dragVector, Math.PI / 4);
|
|
||||||
} else {
|
} else {
|
||||||
item.position += event.delta;
|
item.position = item.data.origPos.add(dragVector);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ const getHoveredItem = function (event, hitOptions) {
|
||||||
|
|
||||||
let hitResult;
|
let hitResult;
|
||||||
for (const result of hitResults) {
|
for (const result of hitResults) {
|
||||||
if (!(result.item.data && result.item.data.noHover) && !hitResult.item.selected) {
|
if (!(result.item.data && result.item.data.noHover) && !result.item.selected) {
|
||||||
hitResult = result;
|
hitResult = result;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,4 +15,4 @@ const getGuideLayer = function () {
|
||||||
return guideLayer;
|
return guideLayer;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default getGuideLayer;
|
export {getGuideLayer};
|
||||||
|
|
|
@ -3,7 +3,8 @@ import keyMirror from 'keymirror';
|
||||||
const Modes = keyMirror({
|
const Modes = keyMirror({
|
||||||
BRUSH: null,
|
BRUSH: null,
|
||||||
ERASER: null,
|
ERASER: null,
|
||||||
LINE: null
|
LINE: null,
|
||||||
|
SELECT: null
|
||||||
});
|
});
|
||||||
|
|
||||||
export default Modes;
|
export default Modes;
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import log from '../log/log';
|
||||||
|
|
||||||
const CHANGE_HOVERED = 'scratch-paint/hover/CHANGE_HOVERED';
|
const CHANGE_HOVERED = 'scratch-paint/hover/CHANGE_HOVERED';
|
||||||
const initialState = null;
|
const initialState = null;
|
||||||
|
|
||||||
|
@ -5,6 +7,10 @@ const reducer = function (state, action) {
|
||||||
if (typeof state === 'undefined') state = initialState;
|
if (typeof state === 'undefined') state = initialState;
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case CHANGE_HOVERED:
|
case CHANGE_HOVERED:
|
||||||
|
if (typeof action.hoveredItem === 'undefined') {
|
||||||
|
log.warn(`Hovered item should not be set to undefined. Use null.`);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
return action.hoveredItem;
|
return action.hoveredItem;
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
|
|
Loading…
Reference in a new issue