diff --git a/src/containers/bit-brush-mode.jsx b/src/containers/bit-brush-mode.jsx index 06978216..3d627822 100644 --- a/src/containers/bit-brush-mode.jsx +++ b/src/containers/bit-brush-mode.jsx @@ -94,7 +94,7 @@ BitBrushMode.propTypes = { const mapStateToProps = state => ({ bitBrushSize: state.scratchPaint.bitBrushSize, - color: state.scratchPaint.color.fillColor, + color: state.scratchPaint.color.fillColor.primary, isBitBrushModeActive: state.scratchPaint.mode === Modes.BIT_BRUSH }); const mapDispatchToProps = dispatch => ({ diff --git a/src/containers/bit-fill-mode.jsx b/src/containers/bit-fill-mode.jsx index 4987bae8..a6d528c7 100644 --- a/src/containers/bit-fill-mode.jsx +++ b/src/containers/bit-fill-mode.jsx @@ -7,8 +7,7 @@ import GradientTypes from '../lib/gradient-types'; import FillModeComponent from '../components/bit-fill-mode/bit-fill-mode.jsx'; -import {changeFillColor, DEFAULT_COLOR} from '../reducers/fill-color'; -import {changeFillColor2} from '../reducers/fill-color-2'; +import {changeFillColor, changeFillColor2, DEFAULT_COLOR} from '../reducers/fill-color'; import {changeMode} from '../reducers/modes'; import {clearSelectedItems} from '../reducers/selected-items'; import {changeGradientType} from '../reducers/fill-mode-gradient-type'; @@ -115,8 +114,8 @@ BitFillMode.propTypes = { const mapStateToProps = state => ({ fillModeGradientType: state.scratchPaint.fillMode.gradientType, // Last user-selected gradient type - color: state.scratchPaint.color.fillColor, - color2: state.scratchPaint.color.fillColor2, + color: state.scratchPaint.color.fillColor.primary, + color2: state.scratchPaint.color.fillColor.secondary, isFillModeActive: state.scratchPaint.mode === Modes.BIT_FILL, selectModeGradientType: state.scratchPaint.color.gradientType }); diff --git a/src/containers/bit-line-mode.jsx b/src/containers/bit-line-mode.jsx index c2a9e9bf..1adef625 100644 --- a/src/containers/bit-line-mode.jsx +++ b/src/containers/bit-line-mode.jsx @@ -94,7 +94,7 @@ BitLineMode.propTypes = { const mapStateToProps = state => ({ bitBrushSize: state.scratchPaint.bitBrushSize, - color: state.scratchPaint.color.fillColor, + color: state.scratchPaint.color.fillColor.primary, isBitLineModeActive: state.scratchPaint.mode === Modes.BIT_LINE }); const mapDispatchToProps = dispatch => ({ diff --git a/src/containers/bit-oval-mode.jsx b/src/containers/bit-oval-mode.jsx index 0a7f356b..edf6d902 100644 --- a/src/containers/bit-oval-mode.jsx +++ b/src/containers/bit-oval-mode.jsx @@ -111,7 +111,7 @@ BitOvalMode.propTypes = { }; const mapStateToProps = state => ({ - color: state.scratchPaint.color.fillColor, + color: state.scratchPaint.color.fillColor.primary, filled: state.scratchPaint.fillBitmapShapes, isOvalModeActive: state.scratchPaint.mode === Modes.BIT_OVAL, selectedItems: state.scratchPaint.selectedItems, diff --git a/src/containers/bit-rect-mode.jsx b/src/containers/bit-rect-mode.jsx index 97c1f5cb..c07d3a9f 100644 --- a/src/containers/bit-rect-mode.jsx +++ b/src/containers/bit-rect-mode.jsx @@ -111,7 +111,7 @@ BitRectMode.propTypes = { }; const mapStateToProps = state => ({ - color: state.scratchPaint.color.fillColor, + color: state.scratchPaint.color.fillColor.primary, filled: state.scratchPaint.fillBitmapShapes, isRectModeActive: state.scratchPaint.mode === Modes.BIT_RECT, selectedItems: state.scratchPaint.selectedItems, diff --git a/src/containers/brush-mode.jsx b/src/containers/brush-mode.jsx index f47a71a9..ba78de65 100644 --- a/src/containers/brush-mode.jsx +++ b/src/containers/brush-mode.jsx @@ -35,9 +35,12 @@ class BrushMode extends React.Component { } else if (!nextProps.isBrushModeActive && this.props.isBrushModeActive) { this.deactivateTool(); } else if (nextProps.isBrushModeActive && this.props.isBrushModeActive) { + const {fillColor, strokeColor, strokeWidth} = nextProps.colorState; this.blob.setOptions({ isEraser: false, - ...nextProps.colorState, + fillColor: fillColor.primary, + strokeColor, + strokeWidth, ...nextProps.brushModeState }); } @@ -56,7 +59,7 @@ class BrushMode extends React.Component { clearSelection(this.props.clearSelectedItems); this.props.clearGradient(); // Force the default brush color if fill is MIXED or transparent - const {fillColor} = this.props.colorState; + const fillColor = this.props.colorState.fillColor.primary; if (fillColor === MIXED || fillColor === null) { this.props.onChangeFillColor(DEFAULT_COLOR); } @@ -86,7 +89,10 @@ BrushMode.propTypes = { clearGradient: PropTypes.func.isRequired, clearSelectedItems: PropTypes.func.isRequired, colorState: PropTypes.shape({ - fillColor: PropTypes.string, + fillColor: PropTypes.shape({ + primary: PropTypes.string, + secondary: PropTypes.string + }), strokeColor: PropTypes.string, strokeWidth: PropTypes.number }).isRequired, diff --git a/src/containers/fill-color-indicator.jsx b/src/containers/fill-color-indicator.jsx index 757e8473..0a4de156 100644 --- a/src/containers/fill-color-indicator.jsx +++ b/src/containers/fill-color-indicator.jsx @@ -5,8 +5,7 @@ import bindAll from 'lodash.bindall'; import parseColor from 'parse-color'; import {changeColorIndex} from '../reducers/color-index'; -import {changeFillColor} from '../reducers/fill-color'; -import {changeFillColor2} from '../reducers/fill-color-2'; +import {changeFillColor, changeFillColor2} from '../reducers/fill-color'; import {changeGradientType} from '../reducers/fill-mode-gradient-type'; import {openFillColor, closeFillColor} from '../reducers/modals'; import {getSelectedLeafItems} from '../helper/selection'; @@ -121,8 +120,8 @@ class FillColorIndicator extends React.Component { const mapStateToProps = state => ({ colorIndex: state.scratchPaint.fillMode.colorIndex, disabled: state.scratchPaint.mode === Modes.LINE, - fillColor: state.scratchPaint.color.fillColor, - fillColor2: state.scratchPaint.color.fillColor2, + fillColor: state.scratchPaint.color.fillColor.primary, + fillColor2: state.scratchPaint.color.fillColor.secondary, fillColorModalVisible: state.scratchPaint.modals.fillColor, format: state.scratchPaint.format, gradientType: state.scratchPaint.color.gradientType, diff --git a/src/containers/fill-mode.jsx b/src/containers/fill-mode.jsx index 1abc9c37..e0f468ae 100644 --- a/src/containers/fill-mode.jsx +++ b/src/containers/fill-mode.jsx @@ -7,8 +7,7 @@ import GradientTypes from '../lib/gradient-types'; import FillTool from '../helper/tools/fill-tool'; import {getRotatedColor, MIXED} from '../helper/style-path'; -import {changeFillColor, DEFAULT_COLOR} from '../reducers/fill-color'; -import {changeFillColor2} from '../reducers/fill-color-2'; +import {changeFillColor, changeFillColor2, DEFAULT_COLOR} from '../reducers/fill-color'; import {changeMode} from '../reducers/modes'; import {clearSelectedItems} from '../reducers/selected-items'; import {clearSelection} from '../helper/selection'; @@ -127,8 +126,8 @@ FillMode.propTypes = { const mapStateToProps = state => ({ fillModeGradientType: state.scratchPaint.fillMode.gradientType, // Last user-selected gradient type - fillColor: state.scratchPaint.color.fillColor, - fillColor2: state.scratchPaint.color.fillColor2, + fillColor: state.scratchPaint.color.fillColor.primary, + fillColor2: state.scratchPaint.color.fillColor.secondary, hoveredItemId: state.scratchPaint.hoveredItemId, isFillModeActive: state.scratchPaint.mode === Modes.FILL, selectModeGradientType: state.scratchPaint.color.gradientType diff --git a/src/containers/line-mode.jsx b/src/containers/line-mode.jsx index ebe011e5..9f2d675e 100644 --- a/src/containers/line-mode.jsx +++ b/src/containers/line-mode.jsx @@ -255,7 +255,10 @@ class LineMode extends React.Component { LineMode.propTypes = { clearSelectedItems: PropTypes.func.isRequired, colorState: PropTypes.shape({ - fillColor: PropTypes.string, + fillColor: PropTypes.shape({ + primary: PropTypes.string, + secondary: PropTypes.string + }), strokeColor: PropTypes.string, strokeWidth: PropTypes.number }).isRequired, diff --git a/src/containers/oval-mode.jsx b/src/containers/oval-mode.jsx index b525ec9a..445c20ef 100644 --- a/src/containers/oval-mode.jsx +++ b/src/containers/oval-mode.jsx @@ -58,7 +58,8 @@ class OvalMode extends React.Component { // If fill and stroke color are both mixed/transparent/absent, set fill to default and stroke to transparent. // If exactly one of fill or stroke color is set, set the other one to transparent. // This way the tool won't draw an invisible state, or be unclear about what will be drawn. - const {fillColor, strokeColor, strokeWidth} = this.props.colorState; + const {strokeColor, strokeWidth} = this.props.colorState; + const fillColor = this.props.colorState.fillColor.primary; const fillColorPresent = fillColor !== MIXED && fillColor !== null; const strokeColorPresent = strokeColor !== MIXED && strokeColor !== null && strokeWidth !== null && strokeWidth !== 0; @@ -98,7 +99,10 @@ OvalMode.propTypes = { clearGradient: PropTypes.func.isRequired, clearSelectedItems: PropTypes.func.isRequired, colorState: PropTypes.shape({ - fillColor: PropTypes.string, + fillColor: PropTypes.shape({ + primary: PropTypes.string, + secondary: PropTypes.string + }), strokeColor: PropTypes.string, strokeWidth: PropTypes.number }).isRequired, diff --git a/src/containers/rect-mode.jsx b/src/containers/rect-mode.jsx index 9675b7f9..14fa8010 100644 --- a/src/containers/rect-mode.jsx +++ b/src/containers/rect-mode.jsx @@ -58,7 +58,8 @@ class RectMode extends React.Component { // If fill and stroke color are both mixed/transparent/absent, set fill to default and stroke to transparent. // If exactly one of fill or stroke color is set, set the other one to transparent. // This way the tool won't draw an invisible state, or be unclear about what will be drawn. - const {fillColor, strokeColor, strokeWidth} = this.props.colorState; + const {strokeColor, strokeWidth} = this.props.colorState; + const fillColor = this.props.colorState.fillColor.primary; const fillColorPresent = fillColor !== MIXED && fillColor !== null; const strokeColorPresent = strokeColor !== MIXED && strokeColor !== null && strokeWidth !== null && strokeWidth !== 0; @@ -98,7 +99,10 @@ RectMode.propTypes = { clearGradient: PropTypes.func.isRequired, clearSelectedItems: PropTypes.func.isRequired, colorState: PropTypes.shape({ - fillColor: PropTypes.string, + fillColor: PropTypes.shape({ + primary: PropTypes.string, + secondary: PropTypes.string + }), strokeColor: PropTypes.string, strokeWidth: PropTypes.number }).isRequired, diff --git a/src/containers/text-mode.jsx b/src/containers/text-mode.jsx index 15a10bdd..d075eb77 100644 --- a/src/containers/text-mode.jsx +++ b/src/containers/text-mode.jsx @@ -82,7 +82,8 @@ class TextMode extends React.Component { // If fill and stroke color are both mixed/transparent/absent, set fill to default and stroke to transparent. // If exactly one of fill or stroke color is set, set the other one to transparent. // This way the tool won't draw an invisible state, or be unclear about what will be drawn. - const {fillColor, strokeColor, strokeWidth} = nextProps.colorState; + const {strokeColor, strokeWidth} = nextProps.colorState; + const fillColor = this.props.colorState.fillColor.primary; const fillColorPresent = fillColor !== MIXED && fillColor !== null; const strokeColorPresent = nextProps.isBitmap ? false : strokeColor !== MIXED && strokeColor !== null && strokeWidth !== null && strokeWidth !== 0; @@ -143,7 +144,10 @@ TextMode.propTypes = { clearGradient: PropTypes.func.isRequired, clearSelectedItems: PropTypes.func.isRequired, colorState: PropTypes.shape({ - fillColor: PropTypes.string, + fillColor: PropTypes.shape({ + primary: PropTypes.string, + secondary: PropTypes.string + }), strokeColor: PropTypes.string, strokeWidth: PropTypes.number }).isRequired, diff --git a/src/helper/style-path.js b/src/helper/style-path.js index 508c2f0c..319c2ea3 100644 --- a/src/helper/style-path.js +++ b/src/helper/style-path.js @@ -502,8 +502,9 @@ const styleCursorPreview = function (path, options) { } }; +// TODO: style using gradient? const styleShape = function (path, options) { - path.fillColor = options.fillColor; + path.fillColor = options.fillColor.primary; path.strokeColor = options.strokeColor; path.strokeWidth = options.strokeWidth; }; diff --git a/src/helper/tools/text-tool.js b/src/helper/tools/text-tool.js index 3c20bc72..d7188846 100644 --- a/src/helper/tools/text-tool.js +++ b/src/helper/tools/text-tool.js @@ -241,7 +241,8 @@ class TextTool extends paper.Tool { content: '', font: this.font, fontSize: 40, - fillColor: this.colorState.fillColor, + // TODO: style using gradient? + fillColor: this.colorState.fillColor.primary, // Default leading for both the HTML text area and paper.PointText // is 120%, but for some reason they are slightly off from each other. // This value was obtained experimentally. diff --git a/src/reducers/color.js b/src/reducers/color.js index 847c310b..ed641f8d 100644 --- a/src/reducers/color.js +++ b/src/reducers/color.js @@ -1,7 +1,6 @@ import {combineReducers} from 'redux'; import eyeDropperReducer from './eye-dropper'; import fillColorReducer from './fill-color'; -import fillColor2Reducer from './fill-color-2'; import gradientTypeReducer from './selection-gradient-type'; import strokeColorReducer from './stroke-color'; import strokeWidthReducer from './stroke-width'; @@ -9,7 +8,6 @@ import strokeWidthReducer from './stroke-width'; export default combineReducers({ eyeDropper: eyeDropperReducer, fillColor: fillColorReducer, - fillColor2: fillColor2Reducer, gradientType: gradientTypeReducer, strokeColor: strokeColorReducer, strokeWidth: strokeWidthReducer diff --git a/src/reducers/fill-color-2.js b/src/reducers/fill-color-2.js deleted file mode 100644 index c655123e..00000000 --- a/src/reducers/fill-color-2.js +++ /dev/null @@ -1,52 +0,0 @@ -import log from '../log/log'; -import {CHANGE_SELECTED_ITEMS} from './selected-items'; -import {CLEAR_GRADIENT} from './selection-gradient-type'; -import {MIXED, getColorsFromSelection} from '../helper/style-path'; -import GradientTypes from '../lib/gradient-types'; - -const CHANGE_FILL_COLOR_2 = 'scratch-paint/fill-color/CHANGE_FILL_COLOR_2'; -// Matches hex colors -const regExp = /^#([0-9a-f]{3}){1,2}$/i; - -const reducer = function (state, action) { - if (typeof state === 'undefined') state = null; - switch (action.type) { - case CHANGE_FILL_COLOR_2: - if (!regExp.test(action.fillColor) && action.fillColor !== null && action.fillColor !== MIXED) { - log.warn(`Invalid hex color code: ${action.fillColor}`); - return state; - } - return action.fillColor; - case CHANGE_SELECTED_ITEMS: - { - // Don't change state if no selection - if (!action.selectedItems || !action.selectedItems.length) { - return state; - } - const colors = getColorsFromSelection(action.selectedItems); - // Gradient type may be solid when multiple gradient types are selected. - // In this case, changing the first color should not change the second color. - if (colors.gradientType !== GradientTypes.SOLID || colors.fillColor2 === MIXED) { - return colors.fillColor2; - } - return state; - } - case CLEAR_GRADIENT: - return null; - default: - return state; - } -}; - -// Action creators ================================== -const changeFillColor2 = function (fillColor) { - return { - type: CHANGE_FILL_COLOR_2, - fillColor: fillColor - }; -}; - -export { - reducer as default, - changeFillColor2 -}; diff --git a/src/reducers/fill-color.js b/src/reducers/fill-color.js index 8b194cae..795c87bb 100644 --- a/src/reducers/fill-color.js +++ b/src/reducers/fill-color.js @@ -1,28 +1,55 @@ import log from '../log/log'; import {CHANGE_SELECTED_ITEMS} from './selected-items'; +import {CLEAR_GRADIENT} from './selection-gradient-type'; import {getColorsFromSelection, MIXED} from '../helper/style-path'; +import GradientTypes from '../lib/gradient-types'; const CHANGE_FILL_COLOR = 'scratch-paint/fill-color/CHANGE_FILL_COLOR'; +const CHANGE_FILL_COLOR_2 = 'scratch-paint/fill-color/CHANGE_FILL_COLOR_2'; const DEFAULT_COLOR = '#9966FF'; -const initialState = DEFAULT_COLOR; +const initialState = { + primary: DEFAULT_COLOR, + secondary: null +}; + // Matches hex colors -const regExp = /^#([0-9a-f]{3}){1,2}$/i; +const hexRegex = /^#([0-9a-f]{3}){1,2}$/i; + +const isValidHexColor = color => { + if (!hexRegex.test(color) && color !== null && color !== MIXED) { + log.warn(`Invalid hex color code: ${color}`); + return false; + } + return true; +}; const reducer = function (state, action) { if (typeof state === 'undefined') state = initialState; switch (action.type) { case CHANGE_FILL_COLOR: - if (!regExp.test(action.fillColor) && action.fillColor !== null && action.fillColor !== MIXED) { - log.warn(`Invalid hex color code: ${action.fillColor}`); - return state; - } - return action.fillColor; - case CHANGE_SELECTED_ITEMS: + if (!isValidHexColor(action.fillColor)) return state; + return {...state, primary: action.fillColor}; + case CHANGE_FILL_COLOR_2: + if (!isValidHexColor(action.fillColor)) return state; + return {...state, secondary: action.fillColor}; + case CHANGE_SELECTED_ITEMS: { // Don't change state if no selection if (!action.selectedItems || !action.selectedItems.length) { return state; } - return getColorsFromSelection(action.selectedItems, action.bitmapMode).fillColor; + const colors = getColorsFromSelection(action.selectedItems, action.bitmapMode); + + const newState = {...state, primary: colors.fillColor}; + + // Gradient type may be solid when multiple gradient types are selected. + // In this case, changing the first color should not change the second color. + if (colors.gradientType !== GradientTypes.SOLID || colors.fillColor2 === MIXED) { + newState.secondary = colors.fillColor2; + } + return newState; + } + case CLEAR_GRADIENT: + return {...state, secondary: null}; default: return state; } @@ -32,12 +59,20 @@ const reducer = function (state, action) { const changeFillColor = function (fillColor) { return { type: CHANGE_FILL_COLOR, - fillColor: fillColor + fillColor + }; +}; + +const changeFillColor2 = function (fillColor) { + return { + type: CHANGE_FILL_COLOR_2, + fillColor }; }; export { reducer as default, changeFillColor, + changeFillColor2, DEFAULT_COLOR };