/** BUG: * - Create a new pixel * - Open a png file * - Draw with the pencil * - Hit CTRL+Z * - RESULT: undo doesn't work, the app can't find the current layer * * - RELATED: when opening an LPE file, the app draws on a layer that is below the one in which the * file is loaded. This is because the data is loaded on new layers, but the first one * isn't removed and sometimes it could have the same ID of a recently added layer. */ /** How the history works * - undoStates stores the states that can be undone * - redoStates stores the states that can be redone * - undo() undoes an action and adds it to the redoStates * - redo() redoes an action and adds it to the undoStates * - Each HistoryState must implement an undo() and redo() function * Those functions actually implement the undo and redo mechanism for that action, * so you'll need to save the data you need as attributes in the constructor. For example, * for the HistoryStateAddColour, the added colour is saved so that it can be removed in * undo() or added back in redo(). * - Each HistoryState must call saveHistoryState(this) so that it gets added to the stack * */ const History = (() => { const undoLogStyle = 'background: #87ff1c; color: black; padding: 5px;'; let undoStates = []; let redoStates = []; Events.on('click', 'undo-button', undo); Events.on('click', 'redo-button', redo); //rename to add undo state function saveHistoryState (state) { //get current canvas data and save to undoStates array undoStates.push(state); //limit the number of states to settings.numberOfHistoryStates if (undoStates.length > Settings.getCurrSettings().numberOfHistoryStates) { undoStates = undoStates.splice(-Settings.getCurrSettings().numberOfHistoryStates, Settings.getCurrSettings().numberOfHistoryStates); } //there is now definitely at least 1 undo state, so the button shouldnt be disabled document.getElementById('undo-button').classList.remove('disabled'); //there should be no redoStates after an undoState is saved redoStates = []; } function undo () { console.log("undoing"); undoOrRedo('undo'); } function redo () { console.log("redoing"); undoOrRedo('redo'); } function undoOrRedo(mode) { if (redoStates.length <= 0 && mode == 'redo') return; if (undoStates.length <= 0 && mode == 'undo') return; // Enable button document.getElementById(mode + '-button').classList.remove('disabled'); if (mode == 'undo') { const undoState = undoStates.pop(); redoStates.push(undoState); undoState.undo(); } else { const redoState = redoStates.pop(); undoStates.push(redoState); redoState.redo(); } // if theres none left, disable the option if (redoStates.length == 0) document.getElementById('redo-button').classList.add('disabled'); if (undoStates.length == 0) document.getElementById('undo-button').classList.add('disabled'); } return { redo, undo, saveHistoryState } })(); class HistoryState { constructor() { History.saveHistoryState(this); } ResizeSprite (xRatio, yRatio, algo, oldData) { this.xRatio = xRatio; this.yRatio = yRatio; this.algo = algo; this.oldData = oldData; this.undo = function() { let layerIndex = 0; currFile.currentAlgo = algo; currFile.resizeSprite(null, [1 / this.xRatio, 1 / this.yRatio]); // Also putting the old data for (let i=0; i this.index + 1) { currFile.layers[this.index + 1].selectLayer(); } else { currFile.layers[this.index - 1].selectLayer(); } this.added.canvas.remove(); this.added.menuEntry.remove(); currFile.layers.splice(index, 1); }; this.redo = function() { currFile.canvasView.append(this.added.canvas); LayerList.getLayerListEntries().prepend(this.added.menuEntry); layers.splice(this.index, 0, this.added); }; } //prototype for undoing canvas changes EditCanvas() { this.canvasState = currFile.currentLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]); this.layerID = currFile.currentLayer.id; this.undo = function () { var stateLayer = LayerList.getLayerByID(this.layerID); var currentCanvas = stateLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]); stateLayer.context.putImageData(this.canvasState, 0, 0); this.canvasState = currentCanvas; stateLayer.updateLayerPreview(); }; this.redo = function () { var stateLayer = LayerList.getLayerByID(this.layerID); var currentCanvas = stateLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]); stateLayer.context.putImageData(this.canvasState, 0, 0); this.canvasState = currentCanvas; stateLayer.updateLayerPreview(); }; } //prototype for undoing added colors AddColor(colorValue) { this.colorValue = colorValue; this.undo = function () { ColorModule.deleteColor(this.colorValue); }; this.redo = function () { ColorModule.addColor(this.colorValue); }; } //prototype for undoing deleted colors DeleteColor(colorValue) { this.colorValue = colorValue; this.canvas = currFile.currentLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]); this.undo = function () { var currentCanvas = currFile.currentLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]); currFile.currentLayer.context.putImageData(this.canvas, 0, 0); ColorModule.addColor(this.colorValue); this.canvas = currentCanvas; }; this.redo = function () { var currentCanvas = currFile.currentLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]); currFile.currentLayer.context.putImageData(this.canvas, 0, 0); ColorModule.deleteColor(this.colorValue); this.canvas = currentCanvas; }; } //prototype for undoing colors edits EditColor(newColorValue, oldColorValue) { this.newColorValue = newColorValue; this.oldColorValue = oldColorValue; this.canvas = currFile.currentLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]); this.undo = function () { let currentCanvas = currFile.currentLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]); currFile.currentLayer.context.putImageData(this.canvas, 0, 0); //find new color in palette and change it back to old color let colors = document.getElementsByClassName('color-button'); for (let i = 0; i < colors.length; i++) { //console.log(newColorValue, '==', colors[i].jscolor.toString()); if (newColorValue == colors[i].jscolor.toString()) { colors[i].jscolor.fromString(oldColorValue); break; } } this.canvas = currentCanvas; }; this.redo = function () { let currentCanvas = currFile.currentLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]); currFile.currentLayer.context.putImageData(this.canvas, 0, 0); //find old color in palette and change it back to new color let colors = document.getElementsByClassName('color-button'); for (let i = 0; i < colors.length; i++) { //console.log(oldColorValue, '==', colors[i].jscolor.toString()); if (oldColorValue == colors[i].jscolor.toString()) { colors[i].jscolor.fromString(newColorValue); break; } } this.canvas = currentCanvas; }; } }