Merge pull request #67 from fsih/toolButtons

Tool buttons
This commit is contained in:
DD Liu 2017-10-16 16:32:38 -04:00 committed by GitHub
commit c49a469eda
19 changed files with 728 additions and 4 deletions

View file

@ -0,0 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import {FormattedMessage} from 'react-intl';
const OvalModeComponent = props => (
<button onClick={props.onMouseDown}>
<FormattedMessage
defaultMessage="Circle"
description="Label for the oval-drawing tool"
id="paint.ovalMode.oval"
/>
</button>
);
OvalModeComponent.propTypes = {
onMouseDown: PropTypes.func.isRequired
};
export default OvalModeComponent;

View file

@ -1,12 +1,19 @@
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 RectMode from '../containers/rect-mode.jsx';
import RoundedRectMode from '../containers/rounded-rect-mode.jsx';
import OvalMode from '../containers/oval-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 +139,10 @@ class PaintEditorComponent extends React.Component {
canvas={this.state.canvas}
onUpdateSvg={this.props.onUpdateSvg}
/>
<PenMode
canvas={this.state.canvas}
onUpdateSvg={this.props.onUpdateSvg}
/>
<LineMode
canvas={this.state.canvas}
onUpdateSvg={this.props.onUpdateSvg}
@ -142,6 +153,15 @@ class PaintEditorComponent extends React.Component {
<ReshapeMode
onUpdateSvg={this.props.onUpdateSvg}
/>
<OvalMode
onUpdateSvg={this.props.onUpdateSvg}
/>
<RectMode
onUpdateSvg={this.props.onUpdateSvg}
/>
<RoundedRectMode
onUpdateSvg={this.props.onUpdateSvg}
/>
</div>
) : null}

View file

@ -0,0 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import {FormattedMessage} from 'react-intl';
const PenModeComponent = props => (
<button onClick={props.onMouseDown}>
<FormattedMessage
defaultMessage="Pen"
description="Label for the pen tool, which draws outlines"
id="paint.penMode.pen"
/>
</button>
);
PenModeComponent.propTypes = {
onMouseDown: PropTypes.func.isRequired
};
export default PenModeComponent;

View file

@ -0,0 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import {FormattedMessage} from 'react-intl';
const RectModeComponent = props => (
<button onClick={props.onMouseDown}>
<FormattedMessage
defaultMessage="Rectangle"
description="Label for the rectangle tool"
id="paint.rectMode.rect"
/>
</button>
);
RectModeComponent.propTypes = {
onMouseDown: PropTypes.func.isRequired
};
export default RectModeComponent;

View file

@ -0,0 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import {FormattedMessage} from 'react-intl';
const RoundedRectModeComponent = props => (
<button onClick={props.onMouseDown}>
<FormattedMessage
defaultMessage="Rounded Rectangle"
description="Label for the rounded rectangle tool"
id="paint.roundedRectMode.roundedRect"
/>
</button>
);
RoundedRectModeComponent.propTypes = {
onMouseDown: PropTypes.func.isRequired
};
export default RoundedRectModeComponent;

View file

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

View file

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

View file

@ -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 OvalTool from '../helper/tools/oval-tool';
import OvalModeComponent from '../components/oval-mode.jsx';
class OvalMode extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'activateTool',
'deactivateTool'
]);
}
componentDidMount () {
if (this.props.isOvalModeActive) {
this.activateTool(this.props);
}
}
componentWillReceiveProps (nextProps) {
if (this.tool && nextProps.hoveredItemId !== this.props.hoveredItemId) {
this.tool.setPrevHoveredItemId(nextProps.hoveredItemId);
}
if (nextProps.isOvalModeActive && !this.props.isOvalModeActive) {
this.activateTool();
} else if (!nextProps.isOvalModeActive && this.props.isOvalModeActive) {
this.deactivateTool();
}
}
shouldComponentUpdate () {
return false; // Static component, for now
}
activateTool () {
this.tool = new OvalTool(
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 (
<OvalModeComponent onMouseDown={this.props.handleMouseDown} />
);
}
}
OvalMode.propTypes = {
clearHoveredItem: PropTypes.func.isRequired,
clearSelectedItems: PropTypes.func.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
});
const mapDispatchToProps = dispatch => ({
setHoveredItem: hoveredItemId => {
dispatch(setHoveredItem(hoveredItemId));
},
clearHoveredItem: () => {
dispatch(clearHoveredItem());
},
clearSelectedItems: () => {
dispatch(clearSelectedItems());
},
setSelectedItems: () => {
dispatch(setSelectedItems(getSelectedLeafItems()));
},
handleMouseDown: () => {
dispatch(changeMode(Modes.OVAL));
},
deactivateTool () {
}
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(OvalMode);

102
src/containers/pen-mode.jsx Normal file
View file

@ -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 (
<PenModeComponent onMouseDown={this.props.handleMouseDown} />
);
}
}
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);

View file

@ -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 RectTool from '../helper/tools/rect-tool';
import RectModeComponent from '../components/rect-mode.jsx';
class RectMode extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'activateTool',
'deactivateTool'
]);
}
componentDidMount () {
if (this.props.isRectModeActive) {
this.activateTool(this.props);
}
}
componentWillReceiveProps (nextProps) {
if (this.tool && nextProps.hoveredItemId !== this.props.hoveredItemId) {
this.tool.setPrevHoveredItemId(nextProps.hoveredItemId);
}
if (nextProps.isRectModeActive && !this.props.isRectModeActive) {
this.activateTool();
} else if (!nextProps.isRectModeActive && this.props.isRectModeActive) {
this.deactivateTool();
}
}
shouldComponentUpdate () {
return false; // Static component, for now
}
activateTool () {
this.tool = new RectTool(
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 (
<RectModeComponent onMouseDown={this.props.handleMouseDown} />
);
}
}
RectMode.propTypes = {
clearHoveredItem: PropTypes.func.isRequired,
clearSelectedItems: PropTypes.func.isRequired,
handleMouseDown: PropTypes.func.isRequired,
hoveredItemId: PropTypes.number,
isRectModeActive: PropTypes.bool.isRequired,
onUpdateSvg: PropTypes.func.isRequired,
setHoveredItem: PropTypes.func.isRequired,
setSelectedItems: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
isRectModeActive: state.scratchPaint.mode === Modes.RECT,
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.RECT));
},
deactivateTool () {
}
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(RectMode);

View file

@ -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 RoundedRectTool from '../helper/tools/rounded-rect-tool';
import RoundedRectModeComponent from '../components/rounded-rect-mode.jsx';
class RoundedRectMode extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
'activateTool',
'deactivateTool'
]);
}
componentDidMount () {
if (this.props.isRoundedRectModeActive) {
this.activateTool(this.props);
}
}
componentWillReceiveProps (nextProps) {
if (this.tool && nextProps.hoveredItemId !== this.props.hoveredItemId) {
this.tool.setPrevHoveredItemId(nextProps.hoveredItemId);
}
if (nextProps.isRoundedRectModeActive && !this.props.isRoundedRectModeActive) {
this.activateTool();
} else if (!nextProps.isRoundedRectModeActive && this.props.isRoundedRectModeActive) {
this.deactivateTool();
}
}
shouldComponentUpdate () {
return false; // Static component, for now
}
activateTool () {
this.tool = new RoundedRectTool(
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 (
<RoundedRectModeComponent onMouseDown={this.props.handleMouseDown} />
);
}
}
RoundedRectMode.propTypes = {
clearHoveredItem: PropTypes.func.isRequired,
clearSelectedItems: PropTypes.func.isRequired,
handleMouseDown: PropTypes.func.isRequired,
hoveredItemId: PropTypes.number,
isRoundedRectModeActive: PropTypes.bool.isRequired,
onUpdateSvg: PropTypes.func.isRequired,
setHoveredItem: PropTypes.func.isRequired,
setSelectedItems: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
isRoundedRectModeActive: state.scratchPaint.mode === Modes.ROUNDED_RECT,
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.ROUNDED_RECT));
},
deactivateTool () {
}
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(RoundedRectMode);

View file

@ -0,0 +1,54 @@
import paper from '@scratch/paper';
import log from '../../log/log';
/**
* Tool for drawing ovals.
*/
class OvalTool 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('Circle tool not yet implemented');
}
handleMouseMove () {
}
handleMouseDrag () {
}
handleMouseUp () {
}
deactivateTool () {
}
}
export default OvalTool;

View file

@ -0,0 +1,54 @@
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 () {
}
deactivateTool () {
}
}
export default PenTool;

View file

@ -0,0 +1,54 @@
import paper from '@scratch/paper';
import log from '../../log/log';
/**
* Tool for drawing rectangles.
*/
class RectTool 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('Rectangle tool not yet implemented');
}
handleMouseMove () {
}
handleMouseDrag () {
}
handleMouseUp () {
}
deactivateTool () {
}
}
export default RectTool;

View file

@ -0,0 +1,54 @@
import paper from '@scratch/paper';
import log from '../../log/log';
/**
* Tool for drawing rounded rectangles
*/
class RoundedRectTool 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('Rounded Rectangle tool not yet implemented');
}
handleMouseMove () {
}
handleMouseDrag () {
}
handleMouseUp () {
}
deactivateTool () {
}
}
export default RoundedRectTool;

View file

@ -5,7 +5,11 @@ const Modes = keyMirror({
ERASER: null,
LINE: null,
SELECT: null,
RESHAPE: null
RESHAPE: null,
PEN: null,
OVAL: null,
RECT: null,
ROUNDED_RECT: null
});
export default Modes;