mirror of
https://github.com/scratchfoundation/scratch-paint.git
synced 2024-12-22 13:32:28 -05:00
Add an undo stack size limit to paint
This commit is contained in:
parent
c39c495f8f
commit
b93ce53694
2 changed files with 84 additions and 2 deletions
|
@ -4,6 +4,7 @@ const UNDO = 'scratch-paint/undo/UNDO';
|
||||||
const REDO = 'scratch-paint/undo/REDO';
|
const REDO = 'scratch-paint/undo/REDO';
|
||||||
const SNAPSHOT = 'scratch-paint/undo/SNAPSHOT';
|
const SNAPSHOT = 'scratch-paint/undo/SNAPSHOT';
|
||||||
const CLEAR = 'scratch-paint/undo/CLEAR';
|
const CLEAR = 'scratch-paint/undo/CLEAR';
|
||||||
|
const MAX_STACK_SIZE = 100;
|
||||||
const initialState = {
|
const initialState = {
|
||||||
stack: [],
|
stack: [],
|
||||||
pointer: -1
|
pointer: -1
|
||||||
|
@ -35,6 +36,14 @@ const reducer = function (state, action) {
|
||||||
log.warn(`Couldn't create undo snapshot, no data provided`);
|
log.warn(`Couldn't create undo snapshot, no data provided`);
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
// Overflowed or about to overflow
|
||||||
|
if (state.pointer >= MAX_STACK_SIZE - 1) {
|
||||||
|
return {
|
||||||
|
// Make a stack of size MAX_STACK_SIZE, cutting off the oldest snapshots.
|
||||||
|
stack: state.stack.slice(state.pointer - MAX_STACK_SIZE + 2, state.pointer + 1).concat(action.snapshot),
|
||||||
|
pointer: MAX_STACK_SIZE - 1
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
// Performing an action clears the redo stack
|
// Performing an action clears the redo stack
|
||||||
stack: state.stack.slice(0, state.pointer + 1).concat(action.snapshot),
|
stack: state.stack.slice(0, state.pointer + 1).concat(action.snapshot),
|
||||||
|
@ -75,5 +84,6 @@ export {
|
||||||
undo,
|
undo,
|
||||||
redo,
|
redo,
|
||||||
undoSnapshot,
|
undoSnapshot,
|
||||||
clearUndoState
|
clearUndoState,
|
||||||
|
MAX_STACK_SIZE
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-env jest */
|
/* eslint-env jest */
|
||||||
import undoReducer from '../../src/reducers/undo';
|
import undoReducer from '../../src/reducers/undo';
|
||||||
import {undoSnapshot, undo, redo, clearUndoState} from '../../src/reducers/undo';
|
import {undoSnapshot, undo, redo, clearUndoState, MAX_STACK_SIZE} from '../../src/reducers/undo';
|
||||||
|
|
||||||
test('initialState', () => {
|
test('initialState', () => {
|
||||||
let defaultState;
|
let defaultState;
|
||||||
|
@ -139,3 +139,75 @@ test('undoSnapshotCantRedo', () => {
|
||||||
expect(newReduxState.stack[0]).toEqual(reduxState.stack[0]);
|
expect(newReduxState.stack[0]).toEqual(reduxState.stack[0]);
|
||||||
expect(newReduxState.stack[1]).toEqual(state3);
|
expect(newReduxState.stack[1]).toEqual(state3);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('snapshotAtMaxStackSize', () => {
|
||||||
|
let defaultState;
|
||||||
|
const getState = function (num) {
|
||||||
|
return {state: num};
|
||||||
|
};
|
||||||
|
// Push MAX_STACK_SIZE states
|
||||||
|
let num = 1;
|
||||||
|
let reduxState = undoReducer(defaultState /* state */, undoSnapshot([getState(num)]) /* action */);
|
||||||
|
for (num = 2; num <= MAX_STACK_SIZE; num++) {
|
||||||
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([getState(num)]) /* action */);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(reduxState.pointer).toEqual(MAX_STACK_SIZE - 1);
|
||||||
|
expect(reduxState.stack).toHaveLength(MAX_STACK_SIZE);
|
||||||
|
expect(reduxState.stack[0].state).toEqual(1);
|
||||||
|
|
||||||
|
// Push one more
|
||||||
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([getState(num)]) /* action */);
|
||||||
|
|
||||||
|
// Stack size stays the same
|
||||||
|
expect(reduxState.pointer).toEqual(MAX_STACK_SIZE - 1);
|
||||||
|
expect(reduxState.stack).toHaveLength(MAX_STACK_SIZE);
|
||||||
|
expect(reduxState.stack[0].state).toEqual(2); // State 1 was cut off
|
||||||
|
expect(reduxState.stack[MAX_STACK_SIZE - 1].state).toEqual(MAX_STACK_SIZE + 1); // Newest added state is at end
|
||||||
|
});
|
||||||
|
|
||||||
|
test('undoRedoAtMaxStackSize', () => {
|
||||||
|
let defaultState;
|
||||||
|
const getState = function (num) {
|
||||||
|
return {state: num};
|
||||||
|
};
|
||||||
|
// Push MAX_STACK_SIZE states
|
||||||
|
let num = 1;
|
||||||
|
let reduxState = undoReducer(defaultState /* state */, undoSnapshot([getState(num)]) /* action */);
|
||||||
|
for (num = 2; num <= MAX_STACK_SIZE; num++) {
|
||||||
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([getState(num)]) /* action */);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Undo twice and redo
|
||||||
|
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
||||||
|
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
||||||
|
reduxState = undoReducer(reduxState /* state */, redo() /* action */);
|
||||||
|
|
||||||
|
expect(reduxState.pointer).toEqual(MAX_STACK_SIZE - 2);
|
||||||
|
expect(reduxState.stack).toHaveLength(MAX_STACK_SIZE);
|
||||||
|
expect(reduxState.stack[0].state).toEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('undoSnapshotAtMaxStackSize', () => {
|
||||||
|
let defaultState;
|
||||||
|
const getState = function (num) {
|
||||||
|
return {state: num};
|
||||||
|
};
|
||||||
|
// Push MAX_STACK_SIZE states
|
||||||
|
let num = 1;
|
||||||
|
let reduxState = undoReducer(defaultState /* state */, undoSnapshot([getState(num)]) /* action */);
|
||||||
|
for (num = 2; num <= MAX_STACK_SIZE; num++) {
|
||||||
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([getState(num)]) /* action */);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Undo twice and then take a snapshot
|
||||||
|
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
||||||
|
reduxState = undoReducer(reduxState /* state */, undo() /* action */);
|
||||||
|
reduxState = undoReducer(reduxState /* state */, undoSnapshot([getState(num)]) /* action */);
|
||||||
|
|
||||||
|
expect(reduxState.pointer).toEqual(MAX_STACK_SIZE - 2);
|
||||||
|
expect(reduxState.stack).toHaveLength(MAX_STACK_SIZE - 1);
|
||||||
|
expect(reduxState.stack[0].state).toEqual(1);
|
||||||
|
expect(reduxState.stack[MAX_STACK_SIZE - 2].state).toEqual(MAX_STACK_SIZE + 1); // Newest added state is at end
|
||||||
|
expect(reduxState.stack[MAX_STACK_SIZE - 3].state).toEqual(MAX_STACK_SIZE - 2); // Old redo state is gone
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in a new issue