oval mode

This commit is contained in:
DD 2017-10-20 14:50:43 -04:00
parent 4fcb908ad5
commit cc350e9056
2 changed files with 98 additions and 39 deletions

View file

@ -5,10 +5,9 @@ import bindAll from 'lodash.bindall';
import Modes from '../modes/modes';
import {changeMode} from '../reducers/modes';
import {clearHoveredItem, setHoveredItem} from '../reducers/hover';
import {clearSelectedItems, setSelectedItems} from '../reducers/selected-items';
import {getSelectedLeafItems} from '../helper/selection';
import {clearSelection, getSelectedLeafItems} from '../helper/selection';
import OvalTool from '../helper/tools/oval-tool';
import OvalModeComponent from '../components/oval-mode/oval-mode.jsx';
@ -26,8 +25,8 @@ class OvalMode extends React.Component {
}
}
componentWillReceiveProps (nextProps) {
if (this.tool && nextProps.hoveredItemId !== this.props.hoveredItemId) {
this.tool.setPrevHoveredItemId(nextProps.hoveredItemId);
if (this.tool && nextProps.colorState !== this.props.colorState) {
this.tool.setColorState(nextProps.colorState);
}
if (nextProps.isOvalModeActive && !this.props.isOvalModeActive) {
@ -40,13 +39,13 @@ class OvalMode extends React.Component {
return nextProps.isOvalModeActive !== this.props.isOvalModeActive;
}
activateTool () {
clearSelection(this.props.clearSelectedItems);
this.tool = new OvalTool(
this.props.setHoveredItem,
this.props.clearHoveredItem,
this.props.setSelectedItems,
this.props.clearSelectedItems,
this.props.onUpdateSvg
);
this.tool.setColorState(this.props.colorState);
this.tool.activate();
}
deactivateTool () {
@ -65,27 +64,23 @@ class OvalMode extends React.Component {
}
OvalMode.propTypes = {
clearHoveredItem: PropTypes.func.isRequired,
clearSelectedItems: PropTypes.func.isRequired,
colorState: PropTypes.shape({
fillColor: PropTypes.string,
strokeColor: PropTypes.string,
strokeWidth: PropTypes.number
}).isRequired,
handleMouseDown: PropTypes.func.isRequired,
hoveredItemId: PropTypes.number,
isOvalModeActive: PropTypes.bool.isRequired,
onUpdateSvg: PropTypes.func.isRequired,
setHoveredItem: PropTypes.func.isRequired,
setSelectedItems: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
isOvalModeActive: state.scratchPaint.mode === Modes.OVAL,
hoveredItemId: state.scratchPaint.hoveredItemId
colorState: state.scratchPaint.color,
isOvalModeActive: state.scratchPaint.mode === Modes.OVAL
});
const mapDispatchToProps = dispatch => ({
setHoveredItem: hoveredItemId => {
dispatch(setHoveredItem(hoveredItemId));
},
clearHoveredItem: () => {
dispatch(clearHoveredItem());
},
clearSelectedItems: () => {
dispatch(clearSelectedItems());
},

View file

@ -1,53 +1,117 @@
import paper from '@scratch/paper';
import log from '../../log/log';
import Modes from '../../modes/modes';
import {styleShape} from '../style-path';
import {clearSelection} from '../selection';
import BoundingBoxTool from '../selection-tools/bounding-box-tool';
/**
* Tool for drawing ovals.
*/
class OvalTool extends paper.Tool {
static get TOLERANCE () {
return 6;
}
/**
* @param {function} setHoveredItem Callback to set the hovered item
* @param {function} clearHoveredItem Callback to clear the hovered item
* @param {function} setSelectedItems Callback to set the set of selected items in the Redux state
* @param {function} clearSelectedItems Callback to clear the set of selected items in the Redux state
* @param {!function} onUpdateSvg A callback to call when the image visibly changes
*/
constructor (setHoveredItem, clearHoveredItem, setSelectedItems, clearSelectedItems, onUpdateSvg) {
constructor (setSelectedItems, clearSelectedItems, onUpdateSvg) {
super();
this.setHoveredItem = setHoveredItem;
this.clearHoveredItem = clearHoveredItem;
this.setSelectedItems = setSelectedItems;
this.clearSelectedItems = clearSelectedItems;
this.onUpdateSvg = onUpdateSvg;
this.prevHoveredItemId = null;
this.boundingBoxTool = new BoundingBoxTool(Modes.OVAL, setSelectedItems, clearSelectedItems, onUpdateSvg);
// We have to set these functions instead of just declaring them because
// paper.js tools hook up the listeners in the setter functions.
this.onMouseDown = this.handleMouseDown;
this.onMouseMove = this.handleMouseMove;
this.onMouseDrag = this.handleMouseDrag;
this.onMouseUp = this.handleMouseUp;
this.downPoint = null;
this.oval = null;
this.colorState = null;
this.isBoundingBoxMode = null;
}
/**
* To be called when the hovered item changes. When the select tool hovers over a
* new item, it compares against this to see if a hover item change event needs to
* be fired.
* @param {paper.Item} prevHoveredItemId ID of the highlight item that indicates the mouse is
* over a given item currently
*/
setPrevHoveredItemId (prevHoveredItemId) {
this.prevHoveredItemId = prevHoveredItemId;
getHitOptions () {
return {
segments: true,
stroke: true,
curves: true,
fill: true,
guide: false,
match: hitResult =>
(hitResult.item.data && hitResult.item.data.isHelperItem) ||
hitResult.item.selected, // Allow hits on bounding box and selected only
tolerance: OvalTool.TOLERANCE / paper.view.zoom
};
}
handleMouseDown () {
log.warn('Circle tool not yet implemented');
setColorState (colorState) {
this.colorState = colorState;
}
handleMouseMove () {
handleMouseDown (event) {
if (this.boundingBoxTool.onMouseDown(event, false /* clone */, false /* multiselect */, this.getHitOptions())) {
this.isBoundingBoxMode = true;
} else {
this.isBoundingBoxMode = false;
clearSelection(this.clearSelectedItems);
this.oval = new paper.Shape.Ellipse({
point: event.downPoint,
size: 0
});
styleShape(this.oval, this.colorState);
}
}
handleMouseDrag () {
handleMouseDrag (event) {
if (event.event.button > 0) return; // only first mouse button
if (this.isBoundingBoxMode) {
this.boundingBoxTool.onMouseDrag(event);
return;
}
const downPoint = new paper.Point(event.downPoint.x, event.downPoint.y);
const point = new paper.Point(event.point.x, event.point.y);
if (event.modifiers.shift) {
this.oval.size = new paper.Point(event.downPoint.x - event.point.x, event.downPoint.x - event.point.x);
} else {
this.oval.size = downPoint.subtract(point);
}
if (event.modifiers.alt) {
this.oval.position = downPoint;
} else {
this.oval.position = downPoint.subtract(this.oval.size.multiply(0.5));
}
}
handleMouseUp () {
handleMouseUp (event) {
if (event.event.button > 0) return; // only first mouse button
if (this.isBoundingBoxMode) {
this.boundingBoxTool.onMouseUp(event);
this.isBoundingBoxMode = null;
return;
}
if (this.oval) {
if (Math.abs(this.oval.size.width * this.oval.size.height) < OvalTool.TOLERANCE / paper.view.zoom) {
// Tiny oval created unintentionally?
this.oval.remove();
this.oval = null;
} else {
const ovalPath = this.oval.toPath(true /* insert */);
this.oval.remove();
this.oval = null;
ovalPath.selected = true;
this.boundingBoxTool.setSelectionBounds();
this.onUpdateSvg();
}
}
}
deactivateTool () {
this.boundingBoxTool.removeBoundsPath();
}
}