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 SNAPSHOT = 'scratch-paint/undo/SNAPSHOT';
|
||||
const CLEAR = 'scratch-paint/undo/CLEAR';
|
||||
const MAX_STACK_SIZE = 100;
|
||||
const initialState = {
|
||||
stack: [],
|
||||
pointer: -1
|
||||
|
@ -35,6 +36,14 @@ const reducer = function (state, action) {
|
|||
log.warn(`Couldn't create undo snapshot, no data provided`);
|
||||
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 {
|
||||
// Performing an action clears the redo stack
|
||||
stack: state.stack.slice(0, state.pointer + 1).concat(action.snapshot),
|
||||
|
@ -75,5 +84,6 @@ export {
|
|||
undo,
|
||||
redo,
|
||||
undoSnapshot,
|
||||
clearUndoState
|
||||
clearUndoState,
|
||||
MAX_STACK_SIZE
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-env jest */
|
||||
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', () => {
|
||||
let defaultState;
|
||||
|
@ -139,3 +139,75 @@ test('undoSnapshotCantRedo', () => {
|
|||
expect(newReduxState.stack[0]).toEqual(reduxState.stack[0]);
|
||||
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