diff --git a/src/components/paint-editor.jsx b/src/components/paint-editor.jsx index f799b9df..f81262b5 100644 --- a/src/components/paint-editor.jsx +++ b/src/components/paint-editor.jsx @@ -1,12 +1,16 @@ import bindAll from 'lodash.bindall'; import React from 'react'; +import PropTypes from 'prop-types'; + import PaperCanvas from '../containers/paper-canvas.jsx'; + import BrushMode from '../containers/brush-mode.jsx'; import EraserMode from '../containers/eraser-mode.jsx'; import ReshapeMode from '../containers/reshape-mode.jsx'; import SelectMode from '../containers/select-mode.jsx'; -import PropTypes from 'prop-types'; import LineMode from '../containers/line-mode.jsx'; +import PenMode from '../containers/pen-mode.jsx'; + import FillColorIndicatorComponent from '../containers/fill-color-indicator.jsx'; import StrokeColorIndicatorComponent from '../containers/stroke-color-indicator.jsx'; import StrokeWidthIndicatorComponent from '../containers/stroke-width-indicator.jsx'; @@ -132,6 +136,10 @@ class PaintEditorComponent extends React.Component { canvas={this.state.canvas} onUpdateSvg={this.props.onUpdateSvg} /> + ( + +); + +PenModeComponent.propTypes = { + onMouseDown: PropTypes.func.isRequired +}; + +export default PenModeComponent; diff --git a/src/containers/brush-mode.jsx b/src/containers/brush-mode.jsx index b4f899ab..0ffdde41 100644 --- a/src/containers/brush-mode.jsx +++ b/src/containers/brush-mode.jsx @@ -3,7 +3,7 @@ import React from 'react'; import {connect} from 'react-redux'; import bindAll from 'lodash.bindall'; import Modes from '../modes/modes'; -import Blobbiness from './blob/blob'; +import Blobbiness from '../helper/blob-tools/blob'; import {changeBrushSize} from '../reducers/brush-mode'; import {changeMode} from '../reducers/modes'; diff --git a/src/containers/eraser-mode.jsx b/src/containers/eraser-mode.jsx index efd920fe..87a7925d 100644 --- a/src/containers/eraser-mode.jsx +++ b/src/containers/eraser-mode.jsx @@ -3,7 +3,7 @@ import React from 'react'; import {connect} from 'react-redux'; import bindAll from 'lodash.bindall'; import Modes from '../modes/modes'; -import Blobbiness from './blob/blob'; +import Blobbiness from '../helper/blob-tools/blob'; import {changeBrushSize} from '../reducers/eraser-mode'; import {clearSelectedItems} from '../reducers/selected-items'; import EraserModeComponent from '../components/eraser-mode.jsx'; diff --git a/src/containers/pen-mode.jsx b/src/containers/pen-mode.jsx new file mode 100644 index 00000000..0519f963 --- /dev/null +++ b/src/containers/pen-mode.jsx @@ -0,0 +1,102 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import {connect} from 'react-redux'; +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 PenTool from '../helper/tools/pen-tool'; +import PenModeComponent from '../components/pen-mode.jsx'; + +class PenMode extends React.Component { + constructor (props) { + super(props); + bindAll(this, [ + 'activateTool', + 'deactivateTool' + ]); + } + componentDidMount () { + if (this.props.isPenModeActive) { + this.activateTool(this.props); + } + } + componentWillReceiveProps (nextProps) { + if (this.tool && nextProps.hoveredItemId !== this.props.hoveredItemId) { + this.tool.setPrevHoveredItemId(nextProps.hoveredItemId); + } + + if (nextProps.isPenModeActive && !this.props.isPenModeActive) { + this.activateTool(); + } else if (!nextProps.isPenModeActive && this.props.isPenModeActive) { + this.deactivateTool(); + } + } + shouldComponentUpdate () { + return false; // Static component, for now + } + activateTool () { + this.tool = new PenTool( + this.props.setHoveredItem, + this.props.clearHoveredItem, + this.props.setSelectedItems, + this.props.clearSelectedItems, + this.props.onUpdateSvg + ); + this.tool.activate(); + } + deactivateTool () { + this.tool.deactivateTool(); + this.tool.remove(); + this.tool = null; + } + render () { + return ( + + ); + } +} + +PenMode.propTypes = { + clearHoveredItem: PropTypes.func.isRequired, + clearSelectedItems: PropTypes.func.isRequired, + handleMouseDown: PropTypes.func.isRequired, + hoveredItemId: PropTypes.number, + isPenModeActive: PropTypes.bool.isRequired, + onUpdateSvg: PropTypes.func.isRequired, + setHoveredItem: PropTypes.func.isRequired, + setSelectedItems: PropTypes.func.isRequired +}; + +const mapStateToProps = state => ({ + isPenModeActive: state.scratchPaint.mode === Modes.PEN, + hoveredItemId: state.scratchPaint.hoveredItemId +}); +const mapDispatchToProps = dispatch => ({ + setHoveredItem: hoveredItemId => { + dispatch(setHoveredItem(hoveredItemId)); + }, + clearHoveredItem: () => { + dispatch(clearHoveredItem()); + }, + clearSelectedItems: () => { + dispatch(clearSelectedItems()); + }, + setSelectedItems: () => { + dispatch(setSelectedItems(getSelectedLeafItems())); + }, + handleMouseDown: () => { + dispatch(changeMode(Modes.PEN)); + }, + deactivateTool () { + } +}); + +export default connect( + mapStateToProps, + mapDispatchToProps +)(PenMode); diff --git a/src/containers/blob/blob.js b/src/helper/blob-tools/blob.js similarity index 100% rename from src/containers/blob/blob.js rename to src/helper/blob-tools/blob.js diff --git a/src/containers/blob/broad-brush-helper.js b/src/helper/blob-tools/broad-brush-helper.js similarity index 100% rename from src/containers/blob/broad-brush-helper.js rename to src/helper/blob-tools/broad-brush-helper.js diff --git a/src/containers/blob/segment-brush-helper.js b/src/helper/blob-tools/segment-brush-helper.js similarity index 100% rename from src/containers/blob/segment-brush-helper.js rename to src/helper/blob-tools/segment-brush-helper.js diff --git a/src/helper/tools/pen-tool.js b/src/helper/tools/pen-tool.js new file mode 100644 index 00000000..76d5e838 --- /dev/null +++ b/src/helper/tools/pen-tool.js @@ -0,0 +1,52 @@ +import paper from '@scratch/paper'; +import log from '../../log/log'; + +/** + * Tool to handle freehand drawing of lines. + */ +class PenTool extends paper.Tool { + /** + * @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) { + super(); + this.setHoveredItem = setHoveredItem; + this.clearHoveredItem = clearHoveredItem; + this.setSelectedItems = setSelectedItems; + this.clearSelectedItems = clearSelectedItems; + this.onUpdateSvg = onUpdateSvg; + this.prevHoveredItemId = null; + + // 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; + } + /** + * 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; + } + handleMouseDown () { + log.warn('Pen not yet implemented'); + } + handleMouseMove () { + } + handleMouseDrag () { + } + handleMouseUp () { + } +} + +export default PenTool; diff --git a/src/modes/modes.js b/src/modes/modes.js index a12446e3..f8262e5c 100644 --- a/src/modes/modes.js +++ b/src/modes/modes.js @@ -5,7 +5,8 @@ const Modes = keyMirror({ ERASER: null, LINE: null, SELECT: null, - RESHAPE: null + RESHAPE: null, + PEN: null }); export default Modes;