update button state when undoing between modes, hide vector tools

This commit is contained in:
DD 2018-04-09 20:10:53 -04:00
parent 6e4ab3191a
commit a6e7fb4251
9 changed files with 100 additions and 29 deletions

View file

@ -150,6 +150,10 @@ $border-radius: 0.25rem;
justify-content: space-between; justify-content: space-between;
} }
.hidden {
display: none;
}
.zoom-controls { .zoom-controls {
display: flex; display: flex;
flex-direction: row-reverse; flex-direction: row-reverse;

View file

@ -35,6 +35,7 @@ import StrokeWidthIndicatorComponent from '../../containers/stroke-width-indicat
import TextMode from '../../containers/text-mode.jsx'; import TextMode from '../../containers/text-mode.jsx';
import Formats from '../../lib/format'; import Formats from '../../lib/format';
import {isVector} from '../../lib/format';
import layout from '../../lib/layout-constants'; import layout from '../../lib/layout-constants';
import styles from './paint-editor.css'; import styles from './paint-editor.css';
@ -342,7 +343,7 @@ const PaintEditorComponent = props => {
<div className={styles.topAlignRow}> <div className={styles.topAlignRow}>
{/* Modes */} {/* Modes */}
{props.canvas !== null ? ( // eslint-disable-line no-negated-condition {props.canvas !== null ? ( // eslint-disable-line no-negated-condition
<div className={styles.modeSelector}> <div className={isVector(props.format) ? styles.modeSelector : styles.hidden}>
<SelectMode <SelectMode
onUpdateSvg={props.onUpdateSvg} onUpdateSvg={props.onUpdateSvg}
/> />
@ -408,7 +409,7 @@ const PaintEditorComponent = props => {
} }
</div> </div>
<div className={styles.canvasControls}> <div className={styles.canvasControls}>
{props.format === Formats.VECTOR ? {isVector(props.format) ?
<Button <Button
className={styles.bitmapButton} className={styles.bitmapButton}
onClick={props.onSwitchToBitmap} onClick={props.onSwitchToBitmap}

View file

@ -23,6 +23,7 @@ import EyeDropperTool from '../helper/tools/eye-dropper';
import Modes from '../lib/modes'; import Modes from '../lib/modes';
import Formats from '../lib/format'; import Formats from '../lib/format';
import {isBitmap} from '../lib/format';
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import bindAll from 'lodash.bindall'; import bindAll from 'lodash.bindall';
@ -93,7 +94,7 @@ class PaintEditor extends React.Component {
resetZoom(); resetZoom();
let raster; let raster;
if (this.props.format === Formats.BITMAP) { if (isBitmap(this.props.format)) {
// @todo export bitmap here // @todo export bitmap here
raster = trim(getRaster()); raster = trim(getRaster());
if (raster.width === 0 || raster.height === 0) { if (raster.width === 0 || raster.height === 0) {
@ -116,13 +117,12 @@ class PaintEditor extends React.Component {
paper.project.view.center.y - bounds.y); paper.project.view.center.y - bounds.y);
showGuideLayers(guideLayers); showGuideLayers(guideLayers);
if (raster) raster.remove();
if (!skipSnapshot) { if (!skipSnapshot) {
performSnapshot(this.props.undoSnapshot); performSnapshot(this.props.undoSnapshot, this.props.format);
} }
if (raster) raster.remove();
// Restore old zoom // Restore old zoom
paper.project.view.zoom = oldZoom; paper.project.view.zoom = oldZoom;
paper.project.view.center = oldCenter; paper.project.view.center = oldCenter;
@ -363,11 +363,11 @@ const mapDispatchToProps = dispatch => ({
// set redux values to default for eye dropper reducer // set redux values to default for eye dropper reducer
dispatch(deactivateEyeDropper()); dispatch(deactivateEyeDropper());
}, },
onUndo: () => { onUndo: format => {
dispatch(undo()); dispatch(undo(format));
}, },
onRedo: () => { onRedo: format => {
dispatch(redo()); dispatch(redo(format));
}, },
undoSnapshot: snapshot => { undoSnapshot: snapshot => {
dispatch(undoSnapshot(snapshot)); dispatch(undoSnapshot(snapshot));

View file

@ -20,6 +20,8 @@ import {clearHoveredItem} from '../reducers/hover';
import {clearPasteOffset} from '../reducers/clipboard'; import {clearPasteOffset} from '../reducers/clipboard';
import {updateViewBounds} from '../reducers/view-bounds'; import {updateViewBounds} from '../reducers/view-bounds';
import {isVector, isBitmap} from '../lib/format';
import styles from './paper-canvas.css'; import styles from './paper-canvas.css';
class PaperCanvas extends React.Component { class PaperCanvas extends React.Component {
@ -50,15 +52,15 @@ class PaperCanvas extends React.Component {
if (this.props.svg) { if (this.props.svg) {
this.importSvg(this.props.svg, this.props.rotationCenterX, this.props.rotationCenterY); this.importSvg(this.props.svg, this.props.rotationCenterX, this.props.rotationCenterY);
} else { } else {
performSnapshot(this.props.undoSnapshot); performSnapshot(this.props.undoSnapshot, this.props.format);
} }
} }
componentWillReceiveProps (newProps) { componentWillReceiveProps (newProps) {
if (this.props.svgId !== newProps.svgId) { if (this.props.svgId !== newProps.svgId) {
this.switchCostume(newProps.svg, newProps.rotationCenterX, newProps.rotationCenterY); this.switchCostume(newProps.svg, newProps.rotationCenterX, newProps.rotationCenterY);
} else if (this.props.format === Formats.VECTOR && newProps.format === Formats.BITMAP) { } else if (isVector(this.props.format) && newProps.format === Formats.BITMAP) {
this.convertToBitmap(); this.convertToBitmap();
} else if (this.props.format === Formats.BITMAP && newProps.format === Formats.VECTOR) { } else if (isBitmap(this.props.format) && newProps.format === Formats.VECTOR) {
this.convertToVector(); this.convertToVector();
} }
} }
@ -121,7 +123,7 @@ class PaperCanvas extends React.Component {
paper.project.view.zoom = oldZoom; paper.project.view.zoom = oldZoom;
paper.project.view.center = oldCenter; paper.project.view.center = oldCenter;
} else { } else {
performSnapshot(this.props.undoSnapshot); performSnapshot(this.props.undoSnapshot, this.props.format);
} }
} }
importSvg (svg, rotationCenterX, rotationCenterY) { importSvg (svg, rotationCenterX, rotationCenterY) {
@ -156,7 +158,7 @@ class PaperCanvas extends React.Component {
if (!item) { if (!item) {
log.error('SVG import failed:'); log.error('SVG import failed:');
log.info(svg); log.info(svg);
performSnapshot(paperCanvas.props.undoSnapshot); performSnapshot(paperCanvas.props.undoSnapshot, paperCanvas.props.format);
return; return;
} }
const itemWidth = item.bounds.width; const itemWidth = item.bounds.width;
@ -198,7 +200,7 @@ class PaperCanvas extends React.Component {
ungroupItems([item]); ungroupItems([item]);
} }
performSnapshot(paperCanvas.props.undoSnapshot); performSnapshot(paperCanvas.props.undoSnapshot, paperCanvas.props.format);
} }
}); });
} }

View file

@ -2,11 +2,20 @@
// modifed from https://github.com/memononen/stylii // modifed from https://github.com/memononen/stylii
import paper from '@scratch/paper'; import paper from '@scratch/paper';
import {hideGuideLayers, showGuideLayers, getRaster} from '../helper/layer'; import {hideGuideLayers, showGuideLayers, getRaster} from '../helper/layer';
import Formats from '../lib/format';
import {isVector, isBitmap} from '../lib/format';
import log from '../log/log';
const performSnapshot = function (dispatchPerformSnapshot) { /**
* Take an undo snapshot
* @param {function} dispatchPerformSnapshot Callback to dispatch a state update
* @param {Formats} either bitmap or vector
*/
const performSnapshot = function (dispatchPerformSnapshot, format) {
const guideLayers = hideGuideLayers(); const guideLayers = hideGuideLayers();
dispatchPerformSnapshot({ dispatchPerformSnapshot({
json: paper.project.exportJSON({asString: false}) json: paper.project.exportJSON({asString: false}),
paintEditorFormat: format
}); });
showGuideLayers(guideLayers); showGuideLayers(guideLayers);
}; };
@ -32,16 +41,28 @@ const _restore = function (entry, setSelectedItems, onUpdateSvg) {
const performUndo = function (undoState, dispatchPerformUndo, setSelectedItems, onUpdateSvg) { const performUndo = function (undoState, dispatchPerformUndo, setSelectedItems, onUpdateSvg) {
if (undoState.pointer > 0) { if (undoState.pointer > 0) {
_restore(undoState.stack[undoState.pointer - 1], setSelectedItems, onUpdateSvg); const state = undoState.stack[undoState.pointer - 1];
dispatchPerformUndo(); _restore(state, setSelectedItems, onUpdateSvg);
const format = isVector(state.paintEditorFormat) ? Formats.UNDO_VECTOR :
isBitmap(state.paintEditorFormat) ? Formats.UNDO_BITMAP : null;
if (!format) {
log.error(`Invalid format: ${state.paintEditorFormat}`);
}
dispatchPerformUndo(format);
} }
}; };
const performRedo = function (undoState, dispatchPerformRedo, setSelectedItems, onUpdateSvg) { const performRedo = function (undoState, dispatchPerformRedo, setSelectedItems, onUpdateSvg) {
if (undoState.pointer >= 0 && undoState.pointer < undoState.stack.length - 1) { if (undoState.pointer >= 0 && undoState.pointer < undoState.stack.length - 1) {
_restore(undoState.stack[undoState.pointer + 1], setSelectedItems, onUpdateSvg); const state = undoState.stack[undoState.pointer + 1];
dispatchPerformRedo(); _restore(state, setSelectedItems, onUpdateSvg);
const format = isVector(state.paintEditorFormat) ? Formats.UNDO_VECTOR :
isBitmap(state.paintEditorFormat) ? Formats.UNDO_BITMAP : null;
if (!format) {
log.error(`Invalid format: ${state.paintEditorFormat}`);
}
dispatchPerformRedo(format);
} }
}; };

View file

@ -2,7 +2,22 @@ import keyMirror from 'keymirror';
const Formats = keyMirror({ const Formats = keyMirror({
BITMAP: null, BITMAP: null,
VECTOR: null VECTOR: null,
// Undo formats are conversions caused by the undo/redo stack
UNDO_BITMAP: null,
UNDO_VECTOR: null
}); });
export default Formats; const isVector = function (format) {
return format === Formats.VECTOR || format === Formats.UNDO_VECTOR;
};
const isBitmap = function (format) {
return format === Formats.BITMAP || format === Formats.UNDO_BITMAP;
};
export {
Formats as default,
isVector,
isBitmap
};

View file

@ -1,5 +1,6 @@
import Formats from '../lib/format'; import Formats from '../lib/format';
import log from '../log/log'; import log from '../log/log';
import {UNDO, REDO} from './undo';
const CHANGE_FORMAT = 'scratch-paint/formats/CHANGE_FORMAT'; const CHANGE_FORMAT = 'scratch-paint/formats/CHANGE_FORMAT';
const initialState = Formats.VECTOR; const initialState = Formats.VECTOR;
@ -7,6 +8,10 @@ const initialState = Formats.VECTOR;
const reducer = function (state, action) { const reducer = function (state, action) {
if (typeof state === 'undefined') state = initialState; if (typeof state === 'undefined') state = initialState;
switch (action.type) { switch (action.type) {
case UNDO:
/* falls through */
case REDO:
/* falls through */
case CHANGE_FORMAT: case CHANGE_FORMAT:
if (action.format in Formats) { if (action.format in Formats) {
return action.format; return action.format;

View file

@ -63,14 +63,24 @@ const undoSnapshot = function (snapshot) {
snapshot: snapshot snapshot: snapshot
}; };
}; };
const undo = function () { /**
* @param {Format} format Either UNDO_VECTOR or UNDO_BITMAP
* @return {Action} undo action
*/
const undo = function (format) {
return { return {
type: UNDO type: UNDO,
format: format
}; };
}; };
const redo = function () { /**
* @param {Format} format Either UNDO_VECTOR or UNDO_BITMAP
* @return {Action} undo action
*/
const redo = function (format) {
return { return {
type: REDO type: REDO,
format: format
}; };
}; };
const clearUndoState = function () { const clearUndoState = function () {
@ -85,5 +95,7 @@ export {
redo, redo,
undoSnapshot, undoSnapshot,
clearUndoState, clearUndoState,
MAX_STACK_SIZE MAX_STACK_SIZE,
UNDO,
REDO
}; };

View file

@ -2,6 +2,7 @@
import Formats from '../../src/lib/format'; import Formats from '../../src/lib/format';
import reducer from '../../src/reducers/format'; import reducer from '../../src/reducers/format';
import {changeFormat} from '../../src/reducers/format'; import {changeFormat} from '../../src/reducers/format';
import {undo, redo} from '../../src/reducers/undo';
test('initialState', () => { test('initialState', () => {
let defaultState; let defaultState;
@ -17,6 +18,16 @@ test('changeFormat', () => {
.toBe(Formats.VECTOR); .toBe(Formats.VECTOR);
}); });
test('undoRedoChangeFormat', () => {
let defaultState;
let reduxState = reducer(defaultState /* state */, changeFormat(Formats.BITMAP) /* action */);
expect(reduxState).toBe(Formats.BITMAP);
reduxState = reducer(reduxState /* state */, undo(Formats.UNDO_BITMAP) /* action */);
expect().toBe(Formats.UNDO_BITMAP);
reduxState = reducer(reduxState /* state */, redo(Formats.UNDO_VECTOR) /* action */);
expect().toBe(Formats.UNDO_VECTOR);
});
test('invalidChangeMode', () => { test('invalidChangeMode', () => {
expect(reducer(Formats.BITMAP /* state */, changeFormat('non-existant mode') /* action */)) expect(reducer(Formats.BITMAP /* state */, changeFormat('non-existant mode') /* action */))
.toBe(Formats.BITMAP); .toBe(Formats.BITMAP);