mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 21:42:30 -05:00
oval mode
This commit is contained in:
parent
4fcb908ad5
commit
cc350e9056
2 changed files with 98 additions and 39 deletions
|
@ -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());
|
||||
},
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue