Get move working

This commit is contained in:
DD 2017-09-13 15:17:59 -04:00
parent 3126c2ca7f
commit 0e91439edd
8 changed files with 75 additions and 63 deletions

View file

@ -4,4 +4,5 @@
margin: auto; margin: auto;
position: relative; position: relative;
background-color: #fff; background-color: #fff;
image-rendering: pixelated;
} }

View file

@ -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 => {

View file

@ -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();

View file

@ -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);
} }
} }
} }

View file

@ -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;
} }

View file

@ -15,4 +15,4 @@ const getGuideLayer = function () {
return guideLayer; return guideLayer;
}; };
export default getGuideLayer; export {getGuideLayer};

View file

@ -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;

View file

@ -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;