mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 13:32:28 -05:00
update button state when undoing between modes, hide vector tools
This commit is contained in:
parent
6e4ab3191a
commit
a6e7fb4251
9 changed files with 100 additions and 29 deletions
|
@ -150,6 +150,10 @@ $border-radius: 0.25rem;
|
|||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.zoom-controls {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
|
|
|
@ -35,6 +35,7 @@ import StrokeWidthIndicatorComponent from '../../containers/stroke-width-indicat
|
|||
import TextMode from '../../containers/text-mode.jsx';
|
||||
|
||||
import Formats from '../../lib/format';
|
||||
import {isVector} from '../../lib/format';
|
||||
import layout from '../../lib/layout-constants';
|
||||
import styles from './paint-editor.css';
|
||||
|
||||
|
@ -342,7 +343,7 @@ const PaintEditorComponent = props => {
|
|||
<div className={styles.topAlignRow}>
|
||||
{/* Modes */}
|
||||
{props.canvas !== null ? ( // eslint-disable-line no-negated-condition
|
||||
<div className={styles.modeSelector}>
|
||||
<div className={isVector(props.format) ? styles.modeSelector : styles.hidden}>
|
||||
<SelectMode
|
||||
onUpdateSvg={props.onUpdateSvg}
|
||||
/>
|
||||
|
@ -408,7 +409,7 @@ const PaintEditorComponent = props => {
|
|||
}
|
||||
</div>
|
||||
<div className={styles.canvasControls}>
|
||||
{props.format === Formats.VECTOR ?
|
||||
{isVector(props.format) ?
|
||||
<Button
|
||||
className={styles.bitmapButton}
|
||||
onClick={props.onSwitchToBitmap}
|
||||
|
|
|
@ -23,6 +23,7 @@ import EyeDropperTool from '../helper/tools/eye-dropper';
|
|||
|
||||
import Modes from '../lib/modes';
|
||||
import Formats from '../lib/format';
|
||||
import {isBitmap} from '../lib/format';
|
||||
import {connect} from 'react-redux';
|
||||
import bindAll from 'lodash.bindall';
|
||||
|
||||
|
@ -93,7 +94,7 @@ class PaintEditor extends React.Component {
|
|||
resetZoom();
|
||||
|
||||
let raster;
|
||||
if (this.props.format === Formats.BITMAP) {
|
||||
if (isBitmap(this.props.format)) {
|
||||
// @todo export bitmap here
|
||||
raster = trim(getRaster());
|
||||
if (raster.width === 0 || raster.height === 0) {
|
||||
|
@ -116,13 +117,12 @@ class PaintEditor extends React.Component {
|
|||
paper.project.view.center.y - bounds.y);
|
||||
|
||||
showGuideLayers(guideLayers);
|
||||
if (raster) raster.remove();
|
||||
|
||||
if (!skipSnapshot) {
|
||||
performSnapshot(this.props.undoSnapshot);
|
||||
performSnapshot(this.props.undoSnapshot, this.props.format);
|
||||
}
|
||||
|
||||
if (raster) raster.remove();
|
||||
|
||||
// Restore old zoom
|
||||
paper.project.view.zoom = oldZoom;
|
||||
paper.project.view.center = oldCenter;
|
||||
|
@ -363,11 +363,11 @@ const mapDispatchToProps = dispatch => ({
|
|||
// set redux values to default for eye dropper reducer
|
||||
dispatch(deactivateEyeDropper());
|
||||
},
|
||||
onUndo: () => {
|
||||
dispatch(undo());
|
||||
onUndo: format => {
|
||||
dispatch(undo(format));
|
||||
},
|
||||
onRedo: () => {
|
||||
dispatch(redo());
|
||||
onRedo: format => {
|
||||
dispatch(redo(format));
|
||||
},
|
||||
undoSnapshot: snapshot => {
|
||||
dispatch(undoSnapshot(snapshot));
|
||||
|
|
|
@ -20,6 +20,8 @@ import {clearHoveredItem} from '../reducers/hover';
|
|||
import {clearPasteOffset} from '../reducers/clipboard';
|
||||
import {updateViewBounds} from '../reducers/view-bounds';
|
||||
|
||||
import {isVector, isBitmap} from '../lib/format';
|
||||
|
||||
import styles from './paper-canvas.css';
|
||||
|
||||
class PaperCanvas extends React.Component {
|
||||
|
@ -50,15 +52,15 @@ class PaperCanvas extends React.Component {
|
|||
if (this.props.svg) {
|
||||
this.importSvg(this.props.svg, this.props.rotationCenterX, this.props.rotationCenterY);
|
||||
} else {
|
||||
performSnapshot(this.props.undoSnapshot);
|
||||
performSnapshot(this.props.undoSnapshot, this.props.format);
|
||||
}
|
||||
}
|
||||
componentWillReceiveProps (newProps) {
|
||||
if (this.props.svgId !== newProps.svgId) {
|
||||
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();
|
||||
} else if (this.props.format === Formats.BITMAP && newProps.format === Formats.VECTOR) {
|
||||
} else if (isBitmap(this.props.format) && newProps.format === Formats.VECTOR) {
|
||||
this.convertToVector();
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +123,7 @@ class PaperCanvas extends React.Component {
|
|||
paper.project.view.zoom = oldZoom;
|
||||
paper.project.view.center = oldCenter;
|
||||
} else {
|
||||
performSnapshot(this.props.undoSnapshot);
|
||||
performSnapshot(this.props.undoSnapshot, this.props.format);
|
||||
}
|
||||
}
|
||||
importSvg (svg, rotationCenterX, rotationCenterY) {
|
||||
|
@ -156,7 +158,7 @@ class PaperCanvas extends React.Component {
|
|||
if (!item) {
|
||||
log.error('SVG import failed:');
|
||||
log.info(svg);
|
||||
performSnapshot(paperCanvas.props.undoSnapshot);
|
||||
performSnapshot(paperCanvas.props.undoSnapshot, paperCanvas.props.format);
|
||||
return;
|
||||
}
|
||||
const itemWidth = item.bounds.width;
|
||||
|
@ -198,7 +200,7 @@ class PaperCanvas extends React.Component {
|
|||
ungroupItems([item]);
|
||||
}
|
||||
|
||||
performSnapshot(paperCanvas.props.undoSnapshot);
|
||||
performSnapshot(paperCanvas.props.undoSnapshot, paperCanvas.props.format);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -2,11 +2,20 @@
|
|||
// modifed from https://github.com/memononen/stylii
|
||||
import paper from '@scratch/paper';
|
||||
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();
|
||||
dispatchPerformSnapshot({
|
||||
json: paper.project.exportJSON({asString: false})
|
||||
json: paper.project.exportJSON({asString: false}),
|
||||
paintEditorFormat: format
|
||||
});
|
||||
showGuideLayers(guideLayers);
|
||||
};
|
||||
|
@ -32,16 +41,28 @@ const _restore = function (entry, setSelectedItems, onUpdateSvg) {
|
|||
|
||||
const performUndo = function (undoState, dispatchPerformUndo, setSelectedItems, onUpdateSvg) {
|
||||
if (undoState.pointer > 0) {
|
||||
_restore(undoState.stack[undoState.pointer - 1], setSelectedItems, onUpdateSvg);
|
||||
dispatchPerformUndo();
|
||||
const state = undoState.stack[undoState.pointer - 1];
|
||||
_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) {
|
||||
if (undoState.pointer >= 0 && undoState.pointer < undoState.stack.length - 1) {
|
||||
_restore(undoState.stack[undoState.pointer + 1], setSelectedItems, onUpdateSvg);
|
||||
dispatchPerformRedo();
|
||||
const state = undoState.stack[undoState.pointer + 1];
|
||||
_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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,22 @@ import keyMirror from 'keymirror';
|
|||
|
||||
const Formats = keyMirror({
|
||||
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
|
||||
};
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import Formats from '../lib/format';
|
||||
import log from '../log/log';
|
||||
import {UNDO, REDO} from './undo';
|
||||
|
||||
const CHANGE_FORMAT = 'scratch-paint/formats/CHANGE_FORMAT';
|
||||
const initialState = Formats.VECTOR;
|
||||
|
@ -7,6 +8,10 @@ const initialState = Formats.VECTOR;
|
|||
const reducer = function (state, action) {
|
||||
if (typeof state === 'undefined') state = initialState;
|
||||
switch (action.type) {
|
||||
case UNDO:
|
||||
/* falls through */
|
||||
case REDO:
|
||||
/* falls through */
|
||||
case CHANGE_FORMAT:
|
||||
if (action.format in Formats) {
|
||||
return action.format;
|
||||
|
|
|
@ -63,14 +63,24 @@ const undoSnapshot = function (snapshot) {
|
|||
snapshot: snapshot
|
||||
};
|
||||
};
|
||||
const undo = function () {
|
||||
/**
|
||||
* @param {Format} format Either UNDO_VECTOR or UNDO_BITMAP
|
||||
* @return {Action} undo action
|
||||
*/
|
||||
const undo = function (format) {
|
||||
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 {
|
||||
type: REDO
|
||||
type: REDO,
|
||||
format: format
|
||||
};
|
||||
};
|
||||
const clearUndoState = function () {
|
||||
|
@ -85,5 +95,7 @@ export {
|
|||
redo,
|
||||
undoSnapshot,
|
||||
clearUndoState,
|
||||
MAX_STACK_SIZE
|
||||
MAX_STACK_SIZE,
|
||||
UNDO,
|
||||
REDO
|
||||
};
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import Formats from '../../src/lib/format';
|
||||
import reducer from '../../src/reducers/format';
|
||||
import {changeFormat} from '../../src/reducers/format';
|
||||
import {undo, redo} from '../../src/reducers/undo';
|
||||
|
||||
test('initialState', () => {
|
||||
let defaultState;
|
||||
|
@ -17,6 +18,16 @@ test('changeFormat', () => {
|
|||
.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', () => {
|
||||
expect(reducer(Formats.BITMAP /* state */, changeFormat('non-existant mode') /* action */))
|
||||
.toBe(Formats.BITMAP);
|
||||
|
|
Loading…
Reference in a new issue