mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 13:32:28 -05:00
add stroke width indicator
This commit is contained in:
parent
e0ed034044
commit
a2cd53b159
11 changed files with 146 additions and 87 deletions
|
@ -7,6 +7,7 @@ import PropTypes from 'prop-types';
|
|||
import LineMode from '../containers/line-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';
|
||||
|
||||
import {defineMessages, injectIntl, intlShape} from 'react-intl';
|
||||
import BufferedInputHOC from './forms/buffered-input-hoc.jsx';
|
||||
|
@ -102,6 +103,8 @@ class PaintEditorComponent extends React.Component {
|
|||
<FillColorIndicatorComponent />
|
||||
{/* stroke */}
|
||||
<StrokeColorIndicatorComponent />
|
||||
{/* stroke width */}
|
||||
<StrokeWidthIndicatorComponent />
|
||||
|
||||
<div className={styles.inputGroup}>
|
||||
Mode tools
|
||||
|
|
30
src/components/stroke-width-indicator.jsx
Normal file
30
src/components/stroke-width-indicator.jsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import BufferedInputHOC from './forms/buffered-input-hoc.jsx';
|
||||
import Input from './forms/input.jsx';
|
||||
import {MAX_STROKE_WIDTH} from '../reducers/stroke-width';
|
||||
|
||||
import styles from './paint-editor.css';
|
||||
|
||||
const BufferedInput = BufferedInputHOC(Input);
|
||||
const StrokeWidthIndicatorComponent = props => (
|
||||
<div className={styles.inputGroup}>
|
||||
<BufferedInput
|
||||
small
|
||||
max={MAX_STROKE_WIDTH}
|
||||
min="0"
|
||||
tabIndex="1"
|
||||
type="number"
|
||||
value={props.strokeWidth}
|
||||
onSubmit={props.onChangeStrokeWidth}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
StrokeWidthIndicatorComponent.propTypes = {
|
||||
onChangeStrokeWidth: PropTypes.func.isRequired,
|
||||
strokeWidth: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default StrokeWidthIndicatorComponent;
|
|
@ -79,7 +79,8 @@ BrushMode.propTypes = {
|
|||
changeBrushSize: PropTypes.func.isRequired,
|
||||
colorState: PropTypes.shape({
|
||||
fillColor: PropTypes.string.isRequired,
|
||||
strokeColor: PropTypes.string.isRequired
|
||||
strokeColor: PropTypes.string.isRequired,
|
||||
strokeWidth: PropTypes.string.isRequired
|
||||
}).isRequired,
|
||||
handleMouseDown: PropTypes.func.isRequired,
|
||||
isBrushModeActive: PropTypes.bool.isRequired,
|
||||
|
|
|
@ -3,7 +3,7 @@ import React from 'react';
|
|||
import {connect} from 'react-redux';
|
||||
import bindAll from 'lodash.bindall';
|
||||
import Modes from '../modes/modes';
|
||||
import {changeLineWidth} from '../reducers/line-mode';
|
||||
import {changeStrokeWidth} from '../reducers/stroke-width';
|
||||
import LineModeComponent from '../components/line-mode.jsx';
|
||||
import {changeMode} from '../reducers/modes';
|
||||
import paper from 'paper';
|
||||
|
@ -50,13 +50,6 @@ class LineMode extends React.Component {
|
|||
this.path = null;
|
||||
this.hitResult = null;
|
||||
|
||||
// TODO add back colors
|
||||
// Make sure a stroke color is set on the line tool
|
||||
// if(!pg.stylebar.getStrokeColor()) {
|
||||
// pg.stylebar.setStrokeColor(pg.stylebar.getFillColor());
|
||||
// pg.stylebar.setFillColor(null);
|
||||
// }
|
||||
|
||||
const lineMode = this;
|
||||
this.tool.onMouseDown = function (event) {
|
||||
if (event.event.button > 0) return; // only first mouse button
|
||||
|
@ -100,9 +93,9 @@ class LineMode extends React.Component {
|
|||
if (!this.path) {
|
||||
this.path = new paper.Path();
|
||||
|
||||
// TODO add back stroke width styling
|
||||
this.path.setStrokeColor(this.props.colorState.strokeColor);
|
||||
this.path.setStrokeWidth(this.props.lineModeState.lineWidth);
|
||||
// Make sure a visible line is drawn
|
||||
this.path.setStrokeWidth(Math.max(1, this.props.colorState.strokeWidth));
|
||||
|
||||
this.path.setSelected(true);
|
||||
this.path.add(event.point);
|
||||
|
@ -260,9 +253,9 @@ class LineMode extends React.Component {
|
|||
}
|
||||
onScroll (event) {
|
||||
if (event.deltaY < 0) {
|
||||
this.props.changeLineWidth(this.props.lineModeState.lineWidth + 1);
|
||||
} else if (event.deltaY > 0 && this.props.lineModeState.lineWidth > 1) {
|
||||
this.props.changeLineWidth(this.props.lineModeState.lineWidth - 1);
|
||||
this.props.changeStrokeWidth(this.props.colorState.strokeWidth + 1);
|
||||
} else if (event.deltaY > 0 && this.props.colorState.strokeWidth > 1) {
|
||||
this.props.changeStrokeWidth(this.props.colorState.strokeWidth - 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -275,27 +268,24 @@ class LineMode extends React.Component {
|
|||
|
||||
LineMode.propTypes = {
|
||||
canvas: PropTypes.instanceOf(Element).isRequired,
|
||||
changeLineWidth: PropTypes.func.isRequired,
|
||||
changeStrokeWidth: PropTypes.func.isRequired,
|
||||
colorState: PropTypes.shape({
|
||||
fillColor: PropTypes.string.isRequired,
|
||||
strokeColor: PropTypes.string.isRequired
|
||||
strokeColor: PropTypes.string.isRequired,
|
||||
strokeWidth: PropTypes.string.isRequired
|
||||
}).isRequired,
|
||||
handleMouseDown: PropTypes.func.isRequired,
|
||||
isLineModeActive: PropTypes.bool.isRequired,
|
||||
lineModeState: PropTypes.shape({
|
||||
lineWidth: PropTypes.number.isRequired
|
||||
}),
|
||||
onUpdateSvg: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
colorState: state.scratchPaint.color,
|
||||
lineModeState: state.scratchPaint.lineMode,
|
||||
isLineModeActive: state.scratchPaint.mode === Modes.LINE
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
changeLineWidth: lineWidth => {
|
||||
dispatch(changeLineWidth(lineWidth));
|
||||
changeStrokeWidth: strokeWidth => {
|
||||
dispatch(changeStrokeWidth(strokeWidth));
|
||||
},
|
||||
handleMouseDown: () => {
|
||||
dispatch(changeMode(Modes.LINE));
|
||||
|
|
31
src/containers/stroke-width-indicator.jsx
Normal file
31
src/containers/stroke-width-indicator.jsx
Normal file
|
@ -0,0 +1,31 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import {connect} from 'react-redux';
|
||||
import {changeStrokeWidth} from '../reducers/stroke-width';
|
||||
import StrokeWidthIndicatorComponent from '../components/stroke-width-indicator.jsx';
|
||||
|
||||
const StrokeWidthIndicator = props => (
|
||||
<StrokeWidthIndicatorComponent
|
||||
strokeWidth={props.strokeWidth}
|
||||
onChangeStrokeWidth={props.handleChangeStrokeWidth}
|
||||
/>
|
||||
);
|
||||
|
||||
StrokeWidthIndicator.propTypes = {
|
||||
handleChangeStrokeWidth: PropTypes.func.isRequired,
|
||||
strokeWidth: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
strokeWidth: state.scratchPaint.color.strokeWidth
|
||||
});
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
handleChangeStrokeWidth: strokeWidth => {
|
||||
dispatch(changeStrokeWidth(strokeWidth));
|
||||
}
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(StrokeWidthIndicator);
|
|
@ -1,8 +1,10 @@
|
|||
import {combineReducers} from 'redux';
|
||||
import fillColorReducer from './fill-color';
|
||||
import strokeColorReducer from './stroke-color';
|
||||
import strokeWidthReducer from './stroke-width';
|
||||
|
||||
export default combineReducers({
|
||||
fillColor: fillColorReducer,
|
||||
strokeColor: strokeColorReducer
|
||||
strokeColor: strokeColorReducer,
|
||||
strokeWidth: strokeWidthReducer
|
||||
});
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import log from '../log/log';
|
||||
|
||||
const CHANGE_LINE_WIDTH = 'scratch-paint/line-mode/CHANGE_LINE_WIDTH';
|
||||
const initialState = {lineWidth: 2};
|
||||
|
||||
const reducer = function (state, action) {
|
||||
if (typeof state === 'undefined') state = initialState;
|
||||
switch (action.type) {
|
||||
case CHANGE_LINE_WIDTH:
|
||||
if (isNaN(action.lineWidth)) {
|
||||
log.warn(`Invalid line width: ${action.lineWidth}`);
|
||||
return state;
|
||||
}
|
||||
return {lineWidth: Math.max(1, action.lineWidth)};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
// Action creators ==================================
|
||||
const changeLineWidth = function (lineWidth) {
|
||||
return {
|
||||
type: CHANGE_LINE_WIDTH,
|
||||
lineWidth: lineWidth
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
reducer as default,
|
||||
changeLineWidth
|
||||
};
|
|
@ -2,13 +2,11 @@ import {combineReducers} from 'redux';
|
|||
import modeReducer from './modes';
|
||||
import brushModeReducer from './brush-mode';
|
||||
import eraserModeReducer from './eraser-mode';
|
||||
import lineModeReducer from './line-mode';
|
||||
import colorReducer from './color';
|
||||
|
||||
export default combineReducers({
|
||||
mode: modeReducer,
|
||||
brushMode: brushModeReducer,
|
||||
eraserMode: eraserModeReducer,
|
||||
lineMode: lineModeReducer,
|
||||
color: colorReducer
|
||||
});
|
||||
|
|
33
src/reducers/stroke-width.js
Normal file
33
src/reducers/stroke-width.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
import log from '../log/log';
|
||||
|
||||
const CHANGE_STROKE_WIDTH = 'scratch-paint/stroke-width/CHANGE_STROKE_WIDTH';
|
||||
const MAX_STROKE_WIDTH = 400;
|
||||
const initialState = 2;
|
||||
|
||||
const reducer = function (state, action) {
|
||||
if (typeof state === 'undefined') state = initialState;
|
||||
switch (action.type) {
|
||||
case CHANGE_STROKE_WIDTH:
|
||||
if (isNaN(action.strokeWidth)) {
|
||||
log.warn(`Invalid brush size: ${action.strokeWidth}`);
|
||||
return state;
|
||||
}
|
||||
return Math.min(MAX_STROKE_WIDTH, Math.max(0, action.strokeWidth));
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
// Action creators ==================================
|
||||
const changeStrokeWidth = function (strokeWidth) {
|
||||
return {
|
||||
type: CHANGE_STROKE_WIDTH,
|
||||
strokeWidth: strokeWidth
|
||||
};
|
||||
};
|
||||
|
||||
export {
|
||||
reducer as default,
|
||||
changeStrokeWidth,
|
||||
MAX_STROKE_WIDTH
|
||||
};
|
|
@ -1,31 +0,0 @@
|
|||
/* eslint-env jest */
|
||||
import lineReducer from '../../src/reducers/line-mode';
|
||||
import {changeLineWidth} from '../../src/reducers/line-mode';
|
||||
|
||||
test('initialState', () => {
|
||||
let defaultState;
|
||||
|
||||
expect(lineReducer(defaultState /* state */, {type: 'anything'} /* action */)).toBeDefined();
|
||||
expect(lineReducer(defaultState /* state */, {type: 'anything'} /* action */).lineWidth).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('changeLineWidth', () => {
|
||||
let defaultState;
|
||||
const newLineWidth = 8078;
|
||||
|
||||
expect(lineReducer(defaultState /* state */, changeLineWidth(newLineWidth) /* action */))
|
||||
.toEqual({lineWidth: newLineWidth});
|
||||
expect(lineReducer(2 /* state */, changeLineWidth(newLineWidth) /* action */))
|
||||
.toEqual({lineWidth: newLineWidth});
|
||||
expect(lineReducer(2 /* state */, changeLineWidth(-1) /* action */))
|
||||
.toEqual({lineWidth: 1});
|
||||
});
|
||||
|
||||
test('invalidChangeLineWidth', () => {
|
||||
const origState = {lineWidth: 2};
|
||||
|
||||
expect(lineReducer(origState /* state */, changeLineWidth('invalid argument') /* action */))
|
||||
.toBe(origState);
|
||||
expect(lineReducer(origState /* state */, changeLineWidth() /* action */))
|
||||
.toBe(origState);
|
||||
});
|
33
test/unit/stroke-width-reducer.test.js
Normal file
33
test/unit/stroke-width-reducer.test.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
/* eslint-env jest */
|
||||
import strokeWidthReducer from '../../src/reducers/stroke-width';
|
||||
import {MAX_STROKE_WIDTH, changeStrokeWidth} from '../../src/reducers/stroke-width';
|
||||
|
||||
test('initialState', () => {
|
||||
let defaultState;
|
||||
|
||||
expect(strokeWidthReducer(defaultState /* state */, {type: 'anything'} /* action */)).toBeDefined();
|
||||
expect(strokeWidthReducer(defaultState /* state */, {type: 'anything'} /* action */)).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
test('changestrokeWidth', () => {
|
||||
let defaultState;
|
||||
const newstrokeWidth = 234;
|
||||
|
||||
expect(strokeWidthReducer(defaultState /* state */, changeStrokeWidth(newstrokeWidth) /* action */))
|
||||
.toEqual(newstrokeWidth);
|
||||
expect(strokeWidthReducer(1 /* state */, changeStrokeWidth(newstrokeWidth) /* action */))
|
||||
.toEqual(newstrokeWidth);
|
||||
expect(strokeWidthReducer(1 /* state */, changeStrokeWidth(-1) /* action */))
|
||||
.toEqual(0);
|
||||
expect(strokeWidthReducer(1 /* state */, changeStrokeWidth(453452352) /* action */))
|
||||
.toEqual(MAX_STROKE_WIDTH);
|
||||
});
|
||||
|
||||
test('invalidChangestrokeWidth', () => {
|
||||
const origState = {strokeWidth: 1};
|
||||
|
||||
expect(strokeWidthReducer(origState /* state */, changeStrokeWidth('invalid argument') /* action */))
|
||||
.toBe(origState);
|
||||
expect(strokeWidthReducer(origState /* state */, changeStrokeWidth() /* action */))
|
||||
.toBe(origState);
|
||||
});
|
Loading…
Reference in a new issue