mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 21:42:30 -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;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
|
|
|
@ -24,12 +24,7 @@ class SelectMode extends React.Component {
|
|||
bindAll(this, [
|
||||
'activateTool',
|
||||
'deactivateTool',
|
||||
'getHitOptions',
|
||||
'preProcessSelection',
|
||||
'onMouseDown',
|
||||
'onMouseMove',
|
||||
'onMouseDrag',
|
||||
'onMouseUp'
|
||||
'getHitOptions'
|
||||
]);
|
||||
this._hitOptions = {
|
||||
segments: true,
|
||||
|
@ -71,48 +66,66 @@ class SelectMode extends React.Component {
|
|||
selectRootItem();
|
||||
this.tool = new paper.Tool();
|
||||
|
||||
|
||||
const selectMode = this;
|
||||
this.tool.onMouseDown = function (event) {
|
||||
if (event.event.button > 0) return; // only first mouse button
|
||||
this.props.clearHoveredItem();
|
||||
if (!this.boundingBoxTool.onMouseDown(
|
||||
event, event.modifiers.alt, event.modifiers.shift, true /* preselectedOnly */)) {
|
||||
this.selectionBoxMode = true;
|
||||
|
||||
selectMode.props.clearHoveredItem();
|
||||
if (!selectMode.boundingBoxTool.onMouseDown(
|
||||
event,
|
||||
event.modifiers.alt,
|
||||
event.modifiers.shift,
|
||||
selectMode.getHitOptions(false /* preseelectedOnly */))) {
|
||||
selectMode.selectionBoxMode = true;
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
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
|
||||
this.selectionRect.removeOnDrag();
|
||||
selectMode.selectionRect.removeOnDrag();
|
||||
} else {
|
||||
this.boundingBoxTool.onMouseDrag(event);
|
||||
selectMode.boundingBoxTool.onMouseDrag(event);
|
||||
}
|
||||
};
|
||||
|
||||
this.tool.onMouseUp = function (event) {
|
||||
if (event.event.button > 0) return; // only first mouse button
|
||||
if (this.selectionBoxMode) {
|
||||
processRectangularSelection(event, this.selectionRect);
|
||||
this.selectionRect.remove();
|
||||
} else {
|
||||
this.boundingBoxTool.onMouseUp(event);
|
||||
this.props.onUpdateSvg();
|
||||
|
||||
if (selectMode.selectionBoxMode) {
|
||||
if (selectMode.selectionRect) {
|
||||
processRectangularSelection(event, selectMode.selectionRect);
|
||||
selectMode.selectionRect.remove();
|
||||
}
|
||||
this.selectionBoxMode = false;
|
||||
this.selectionRect = null;
|
||||
selectMode.boundingBoxTool.setSelectionBounds();
|
||||
} else {
|
||||
selectMode.boundingBoxTool.onMouseUp(event);
|
||||
selectMode.props.onUpdateSvg();
|
||||
}
|
||||
selectMode.selectionBoxMode = false;
|
||||
selectMode.selectionRect = null;
|
||||
};
|
||||
this.tool.activate();
|
||||
}
|
||||
deactivateTool () {
|
||||
this.props.setHoveredItem();
|
||||
this.props.clearHoveredItem();
|
||||
this.tool.remove();
|
||||
this.tool = null;
|
||||
this.hitResult = null;
|
||||
|
@ -127,13 +140,15 @@ class SelectMode extends React.Component {
|
|||
SelectMode.propTypes = {
|
||||
clearHoveredItem: PropTypes.func.isRequired,
|
||||
handleMouseDown: PropTypes.func.isRequired,
|
||||
hoveredItem: PropTypes.instanceOf(paper.Item),
|
||||
isSelectModeActive: PropTypes.bool.isRequired,
|
||||
onUpdateSvg: PropTypes.func.isRequired,
|
||||
setHoveredItem: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
isSelectModeActive: state.scratchPaint.mode === Modes.SELECT
|
||||
isSelectModeActive: state.scratchPaint.mode === Modes.SELECT,
|
||||
hoveredItem: state.scratchPaint.hoveredItem
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
setHoveredItem: hoveredItem => {
|
||||
|
|
|
@ -40,17 +40,18 @@ class BoundingBoxTool {
|
|||
* @param {!MouseEvent} event The mouse event
|
||||
* @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} 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
|
||||
*/
|
||||
onMouseDown (event, clone, multiselect, preselectedOnly) {
|
||||
const hitResults = paper.project.hitTestAll(event.point, this.getHitOptions(preselectedOnly));
|
||||
onMouseDown (event, clone, multiselect, hitOptions) {
|
||||
const hitResults = paper.project.hitTestAll(event.point, hitOptions);
|
||||
if (!hitResults || hitResults.length === 0) {
|
||||
if (!multiselect) {
|
||||
this.removeBoundsPath();
|
||||
clearSelection();
|
||||
}
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prefer scale to trigger over rotate, and scale and rotate to trigger over other hits
|
||||
|
@ -65,14 +66,17 @@ class BoundingBoxTool {
|
|||
hitResult = hitResults[i];
|
||||
this.mode = Modes.ROTATE;
|
||||
this._modeMap[this.mode].onMouseDown(hitResult, this.boundsPath, getSelectedItems());
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.mode) {
|
||||
this.mode = Modes.MOVE;
|
||||
this._modeMap[this.mode].onMouseDown(hitResult, clone, multiselect);
|
||||
}
|
||||
}
|
||||
|
||||
// while transforming object, never show the bounds stuff
|
||||
this.removeBoundsPath();
|
||||
return true;
|
||||
}
|
||||
onMouseDrag (event) {
|
||||
if (event.event.button > 0) return; // only first mouse button
|
||||
|
@ -83,13 +87,8 @@ class BoundingBoxTool {
|
|||
this._modeMap[this.mode].onMouseUp(event);
|
||||
|
||||
this.mode = null;
|
||||
|
||||
if (getSelectedItems().length <= 0) {
|
||||
this.removeBoundsPath();
|
||||
} else {
|
||||
this.setSelectionBounds();
|
||||
}
|
||||
}
|
||||
setSelectionBounds () {
|
||||
this.removeBoundsPath();
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {isGroup} from '../group';
|
||||
import {isCompoundPathItem, getRootItem} from '../item';
|
||||
import {snapDeltaToAngle} from '../math';
|
||||
import {clearSelection, cloneSelection, getSelectedItems, setItemSelection, setGroupSelection} from '../selection';
|
||||
import {clearSelection, cloneSelection, getSelectedItems, setItemSelection} from '../selection';
|
||||
|
||||
class MoveTool {
|
||||
constructor () {
|
||||
|
@ -14,31 +14,22 @@ class MoveTool {
|
|||
* @param {boolean} multiselect Whether to multiselect on mouse down (e.g. shift key held)
|
||||
*/
|
||||
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
|
||||
if (!multiselect) {
|
||||
clearSelection();
|
||||
}
|
||||
// also needs some special love for compound paths and groups,
|
||||
// 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);
|
||||
setItemSelection(item, true);
|
||||
} else if (multiselect) {
|
||||
setGroupSelection(root, false);
|
||||
}
|
||||
} else if (multiselect && hitResult.item.selected) {
|
||||
setItemSelection(hitResult.item, false);
|
||||
} else {
|
||||
setItemSelection(hitResult.item, true);
|
||||
setItemSelection(item, false);
|
||||
}
|
||||
if (clone) cloneSelection();
|
||||
this.selectedItems = getSelectedItems();
|
||||
}
|
||||
onMouseDrag (event) {
|
||||
const dragVector = (event.point - event.downPoint);
|
||||
|
||||
const dragVector = event.point.subtract(event.downPoint);
|
||||
for (const item of this.selectedItems) {
|
||||
// add the position of the item before the drag started
|
||||
// for later use in the snap calculation
|
||||
|
@ -47,10 +38,9 @@ class MoveTool {
|
|||
}
|
||||
|
||||
if (event.modifiers.shift) {
|
||||
item.position = item.data.origPos +
|
||||
snapDeltaToAngle(dragVector, Math.PI / 4);
|
||||
item.position = item.data.origPos.add(snapDeltaToAngle(dragVector, Math.PI / 4));
|
||||
} else {
|
||||
item.position += event.delta;
|
||||
item.position = item.data.origPos.add(dragVector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ const getHoveredItem = function (event, hitOptions) {
|
|||
|
||||
let hitResult;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -15,4 +15,4 @@ const getGuideLayer = function () {
|
|||
return guideLayer;
|
||||
};
|
||||
|
||||
export default getGuideLayer;
|
||||
export {getGuideLayer};
|
||||
|
|
|
@ -3,7 +3,8 @@ import keyMirror from 'keymirror';
|
|||
const Modes = keyMirror({
|
||||
BRUSH: null,
|
||||
ERASER: null,
|
||||
LINE: null
|
||||
LINE: null,
|
||||
SELECT: null
|
||||
});
|
||||
|
||||
export default Modes;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import log from '../log/log';
|
||||
|
||||
const CHANGE_HOVERED = 'scratch-paint/hover/CHANGE_HOVERED';
|
||||
const initialState = null;
|
||||
|
||||
|
@ -5,6 +7,10 @@ const reducer = function (state, action) {
|
|||
if (typeof state === 'undefined') state = initialState;
|
||||
switch (action.type) {
|
||||
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;
|
||||
default:
|
||||
return state;
|
||||
|
|
Loading…
Reference in a new issue