From c2a3ccc8d0fb721ed3f8a0b97f7e4d0411068c9c Mon Sep 17 00:00:00 2001 From: juliandescottes Date: Sun, 20 Apr 2014 13:15:30 +0200 Subject: [PATCH] Simplified SAVE STATE events, added wrap method to easily build decorators --- src/js/controller/piskel/PiskelController.js | 13 ++ .../piskel/PublicPiskelController.js | 114 ++++-------------- src/js/drawingtools/BaseTool.js | 12 +- src/js/drawingtools/Move.js | 10 +- src/js/drawingtools/PaintBucket.js | 12 +- src/js/drawingtools/ShapeTool.js | 17 +-- src/js/drawingtools/SimplePen.js | 10 +- src/js/drawingtools/Stroke.js | 10 +- src/js/selection/SelectionManager.js | 19 +-- src/js/service/HistoryService.js | 95 ++++++--------- src/js/utils/core.js | 8 ++ 11 files changed, 119 insertions(+), 201 deletions(-) diff --git a/src/js/controller/piskel/PiskelController.js b/src/js/controller/piskel/PiskelController.js index 494555c2..0e07db67 100644 --- a/src/js/controller/piskel/PiskelController.js +++ b/src/js/controller/piskel/PiskelController.js @@ -56,6 +56,19 @@ return layer.getFrameAt(this.currentFrameIndex); }; + + ns.PiskelController.prototype.getCurrentLayerIndex = function () { + return this.currentLayerIndex; + }; + + ns.PiskelController.prototype.getCurrentFrameIndex = function () { + return this.currentFrameIndex; + }; + + ns.PiskelController.prototype.getPiskel = function () { + return this.piskel; + }; + ns.PiskelController.prototype.getFrameAt = function (index) { var frames = this.getLayers().map(function (l) { return l.getFrameAt(index); diff --git a/src/js/controller/piskel/PublicPiskelController.js b/src/js/controller/piskel/PublicPiskelController.js index f43a3f6d..fc1bd2b7 100644 --- a/src/js/controller/piskel/PublicPiskelController.js +++ b/src/js/controller/piskel/PublicPiskelController.js @@ -3,6 +3,7 @@ ns.PublicPiskelController = function (piskelController) { this.piskelController = piskelController; + pskl.utils.wrap(this, this.piskelController); }; ns.PublicPiskelController.prototype.init = function () { @@ -32,22 +33,13 @@ ns.PublicPiskelController.prototype.addFrameAt = function (index) { this.piskelController.addFrameAt(index); - - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'ADD_FRAME', - index : index - }); - + this.raiseSaveStateEvent_(this.piskelController.addFrameAt, [index]); $.publish(Events.PISKEL_RESET); }; ns.PublicPiskelController.prototype.removeFrameAt = function (index) { this.piskelController.removeFrameAt(index); - - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'DELETE_FRAME', - index : index - }); + this.raiseSaveStateEvent_(this.piskelController.removeFrameAt, [index]); $.publish(Events.PISKEL_RESET); }; @@ -55,32 +47,33 @@ this.piskelController.duplicateFrameAt(this.getCurrentFrameIndex()); }; + ns.PublicPiskelController.prototype.raiseSaveStateEvent_ = function (fn, args) { + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'REPLAY', + scope : this, + replay : { + fn : fn, + args : args + } + }); + }; + + ns.PublicPiskelController.prototype.replay = function (frame, replayData) { + replayData.fn.apply(this.piskelController, replayData.args); + }; + ns.PublicPiskelController.prototype.duplicateFrameAt = function (index) { this.piskelController.duplicateFrameAt(index); - - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'DUPLICATE_FRAME', - index : index - }); - + this.raiseSaveStateEvent_(this.piskelController.duplicateFrameAt, [index]); $.publish(Events.PISKEL_RESET); }; ns.PublicPiskelController.prototype.moveFrame = function (fromIndex, toIndex) { this.piskelController.moveFrame(fromIndex, toIndex); - - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'MOVE_FRAME', - from : fromIndex, - to : toIndex - }); + this.raiseSaveStateEvent_(this.piskelController.moveFrame, [fromIndex, toIndex]); $.publish(Events.PISKEL_RESET); }; - ns.PublicPiskelController.prototype.getFrameCount = function () { - return this.piskelController.getFrameCount(); - }; - ns.PublicPiskelController.prototype.setCurrentFrameIndex = function (index) { this.piskelController.setCurrentFrameIndex(index); $.publish(Events.PISKEL_RESET); @@ -108,86 +101,37 @@ ns.PublicPiskelController.prototype.renameLayerAt = function (index, name) { this.piskelController.renameLayerAt(index, name); - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'RENAME_LAYER', - index : index, - name : name - }); - }; - - ns.PublicPiskelController.prototype.getLayerByIndex = function (index) { - return this.piskelController.getLayerByIndex(index); + this.raiseSaveStateEvent_(this.piskelController.renameLayerAt, [index, name]); }; ns.PublicPiskelController.prototype.createLayer = function (name) { this.piskelController.createLayer(name); - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'CREATE_LAYER', - name : name - }); + this.raiseSaveStateEvent_(this.piskelController.createLayer, [name]); $.publish(Events.PISKEL_RESET); }; ns.PublicPiskelController.prototype.moveLayerUp = function () { this.piskelController.moveLayerUp(); - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'LAYER_UP' - }); + this.raiseSaveStateEvent_(this.piskelController.moveLayerUp, []); $.publish(Events.PISKEL_RESET); }; ns.PublicPiskelController.prototype.moveLayerDown = function () { this.piskelController.moveLayerDown(); - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'LAYER_DOWN' - }); + this.raiseSaveStateEvent_(this.piskelController.moveLayerDown, []); $.publish(Events.PISKEL_RESET); }; ns.PublicPiskelController.prototype.removeCurrentLayer = function () { this.piskelController.removeCurrentLayer(); - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'REMOVE_LAYER' - }); + this.raiseSaveStateEvent_(this.piskelController.removeCurrentLayer, []); $.publish(Events.PISKEL_RESET); }; - ns.PublicPiskelController.prototype.serialize = function (compressed) { - return this.piskelController.serialize(compressed); - }; - - ns.PublicPiskelController.prototype.getHeight = function () { - return this.piskelController.getHeight(); - }; - - ns.PublicPiskelController.prototype.getWidth = function () { - return this.piskelController.getWidth(); - }; - - ns.PublicPiskelController.prototype.getFPS = function () { - return this.piskelController.getFPS(); - }; - - ns.PublicPiskelController.prototype.getLayers = function () { - return this.piskelController.getLayers(); - }; - - ns.PublicPiskelController.prototype.getCurrentLayer = function () { - return this.piskelController.getCurrentLayer(); - }; - ns.PublicPiskelController.prototype.getCurrentLayerIndex = function () { return this.piskelController.currentLayerIndex; }; - ns.PublicPiskelController.prototype.getLayerAt = function (index) { - return this.piskelController.getLayerAt(index); - }; - - ns.PublicPiskelController.prototype.getCurrentFrame = function () { - return this.piskelController.getCurrentFrame(); - }; - ns.PublicPiskelController.prototype.getCurrentFrameIndex = function () { return this.piskelController.currentFrameIndex; }; @@ -196,12 +140,4 @@ return this.piskelController.piskel; }; - ns.PublicPiskelController.prototype.getFrameAt = function (index) { - return this.piskelController.getFrameAt(index); - }; - - ns.PublicPiskelController.prototype.hasFrameAt = function (index) { - return this.piskelController.hasFrameAt(index); - }; - })(); \ No newline at end of file diff --git a/src/js/drawingtools/BaseTool.js b/src/js/drawingtools/BaseTool.js index 9ab770da..ff077157 100644 --- a/src/js/drawingtools/BaseTool.js +++ b/src/js/drawingtools/BaseTool.js @@ -50,12 +50,12 @@ } }; - ns.BaseTool.prototype.raiseSaveStateEvent = function (args) { - var toolInfo = { - toolId : this.toolId, - args : args - }; - $.publish(Events.PISKEL_SAVE_STATE, toolInfo); + ns.BaseTool.prototype.raiseSaveStateEvent = function (replayData) { + $.publish(Events.PISKEL_SAVE_STATE, { + type : 'REPLAY', + scope : this, + replay : replayData + }); }; diff --git a/src/js/drawingtools/Move.js b/src/js/drawingtools/Move.js index 73e32d75..630a5729 100644 --- a/src/js/drawingtools/Move.js +++ b/src/js/drawingtools/Move.js @@ -53,13 +53,9 @@ ns.Move.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) { this.moveToolAt(col, row, color, frame, overlay); - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'TOOL', - tool : this, - replay : { - colDiff : col - this.startCol, - rowDiff : row - this.startRow - } + this.raiseSaveStateEvent({ + colDiff : col - this.startCol, + rowDiff : row - this.startRow }); }; diff --git a/src/js/drawingtools/PaintBucket.js b/src/js/drawingtools/PaintBucket.js index f8cbabea..c2ece067 100644 --- a/src/js/drawingtools/PaintBucket.js +++ b/src/js/drawingtools/PaintBucket.js @@ -19,14 +19,10 @@ ns.PaintBucket.prototype.applyToolAt = function(col, row, color, frame, overlay, event) { pskl.PixelUtils.paintSimilarConnectedPixelsFromFrame(frame, col, row, color); - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'TOOL', - tool : this, - replay : { - col : col, - row : row, - color : color - } + this.raiseSaveStateEvent({ + col : col, + row : row, + color : color }); }; diff --git a/src/js/drawingtools/ShapeTool.js b/src/js/drawingtools/ShapeTool.js index a069ebd9..a99965d2 100644 --- a/src/js/drawingtools/ShapeTool.js +++ b/src/js/drawingtools/ShapeTool.js @@ -51,17 +51,12 @@ this.draw_(coords.col, coords.row, color, frame); $.publish(Events.DRAG_END, [col, row]); - - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'TOOL', - tool : this, - replay : { - col : col, - row : row, - startCol : this.startCol, - startRow : this.startRow, - color : color - } + this.raiseSaveStateEvent({ + col : col, + row : row, + startCol : this.startCol, + startRow : this.startRow, + color : color }); }; diff --git a/src/js/drawingtools/SimplePen.js b/src/js/drawingtools/SimplePen.js index fa2f9da5..82532d5a 100644 --- a/src/js/drawingtools/SimplePen.js +++ b/src/js/drawingtools/SimplePen.js @@ -55,13 +55,9 @@ ns.SimplePen.prototype.releaseToolAt = function(col, row, color, frame, overlay, event) { - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'TOOL', - tool : this, - replay : { - pixels : this.pixels.slice(0), - color : color - } + this.raiseSaveStateEvent({ + pixels : this.pixels.slice(0), + color : color }); this.pixels = []; }; diff --git a/src/js/drawingtools/Stroke.js b/src/js/drawingtools/Stroke.js index c684c3f3..27c115ae 100644 --- a/src/js/drawingtools/Stroke.js +++ b/src/js/drawingtools/Stroke.js @@ -73,13 +73,9 @@ // For now, we are done with the stroke tool and don't need an overlay anymore: overlay.clear(); - $.publish(Events.PISKEL_SAVE_STATE, { - type : 'TOOL', - tool : this, - replay : { - pixels : strokePoints, - color : color - } + this.raiseSaveStateEvent({ + pixels : strokePoints, + color : color }); }; diff --git a/src/js/selection/SelectionManager.js b/src/js/selection/SelectionManager.js index c173b9f2..07959e2c 100644 --- a/src/js/selection/SelectionManager.js +++ b/src/js/selection/SelectionManager.js @@ -1,6 +1,11 @@ (function () { var ns = $.namespace("pskl.selection"); + var SELECTION_REPLAY = { + PASTE : 'REPLAY_PASTE', + ERASE : 'REPLAY_ERASE' + }; + ns.SelectionManager = function (piskelController) { this.piskelController = piskelController; @@ -55,10 +60,10 @@ } $.publish(Events.PISKEL_SAVE_STATE, { - type : 'TOOL', - tool : this, + type : 'REPLAY', + scope : this, replay : { - type : 'erase', + type : SELECTION_REPLAY.ERASE, pixels : JSON.parse(JSON.stringify(pixels.slice(0))) } }); @@ -81,10 +86,10 @@ var currentFrame = this.piskelController.getCurrentFrame(); $.publish(Events.PISKEL_SAVE_STATE, { - type : 'TOOL', - tool : this, + type : 'REPLAY', + scope : this, replay : { - type : 'paste', + type : SELECTION_REPLAY.PASTE, pixels : JSON.parse(JSON.stringify(pixels.slice(0))) } }); @@ -98,7 +103,7 @@ ns.SelectionManager.prototype.replay = function (frame, replayData) { var pixels = replayData.pixels; pixels.forEach(function (pixel) { - var color = replayData.type === 'paste' ? pixel.color : Constants.TRANSPARENT_COLOR; + var color = replayData.type === SELECTION_REPLAY.PASTE ? pixel.color : Constants.TRANSPARENT_COLOR; frame.setPixel(pixel.col, pixel.row, color); }); }; diff --git a/src/js/service/HistoryService.js b/src/js/service/HistoryService.js index 5bf60685..9d7c435c 100644 --- a/src/js/service/HistoryService.js +++ b/src/js/service/HistoryService.js @@ -2,6 +2,7 @@ var ns = $.namespace('pskl.service'); var SNAPSHOT_PERIOD = 50; + var LOAD_STATE_INTERVAL = 50; ns.HistoryService = function (piskelController) { this.piskelController = piskelController; @@ -9,7 +10,7 @@ this.currentIndex = -1; this.saveState__b = this.saveState.bind(this); - this.lastEvent = -1; + this.lastLoadState = -1; }; ns.HistoryService.prototype.init = function () { @@ -38,46 +39,44 @@ }; ns.HistoryService.prototype.undo = function () { - var now = Date.now(); - if ((Date.now() - this.lastEvent) > 50 && this.currentIndex > 0) { - this.currentIndex = this.currentIndex - 1; - this.loadState(this.currentIndex); - this.lastEvent = Date.now(); - } + this.loadState(this.currentIndex - 1); }; ns.HistoryService.prototype.redo = function () { - var now = Date.now(); - if ((Date.now() - this.lastEvent) > 50 && this.currentIndex < this.stateQueue.length - 1) { - this.currentIndex = this.currentIndex + 1; - this.loadState(this.currentIndex); - this.lastEvent = Date.now(); - } + this.loadState(this.currentIndex + 1); }; ns.HistoryService.prototype.loadState = function (index) { - // get nearest snaphot index - var snapshotIndex = -1; - for (var i = index ; i >= 0 ; i--) { - if (this.stateQueue[i].piskel) { - snapshotIndex = i; - break; + if (this.isLoadStateAllowed_(index)) { + this.lastLoadState = Date.now(); + + var snapshotIndex = this.getPreviousSnapshotIndex_(index); + if (snapshotIndex < 0) { + throw 'Could not find previous SNAPSHOT saved in history stateQueue'; } + + var serializedPiskel = this.stateQueue[snapshotIndex].piskel; + + if (typeof serializedPiskel === "string") { + this.stateQueue[snapshotIndex].piskel = JSON.parse(serializedPiskel); + serializedPiskel = this.stateQueue[snapshotIndex].piskel; + } + + this.loadPiskel(serializedPiskel, this.onPiskelLoadedCallback.bind(this, index, snapshotIndex)); } + }; - if (snapshotIndex === -1) { - throw 'Could not find previous SNAPSHOT saved in history stateQueue'; + ns.HistoryService.prototype.isLoadStateAllowed_ = function (index) { + var timeOk = (Date.now() - this.lastLoadState) > LOAD_STATE_INTERVAL; + var indexInRange = index >= 0 && index < this.stateQueue.length; + return timeOk && indexInRange; + }; + + ns.HistoryService.prototype.getPreviousSnapshotIndex_ = function (index) { + while (this.stateQueue[index] && !this.stateQueue[index].piskel) { + index = index - 1; } - - var serializedPiskel = this.stateQueue[snapshotIndex].piskel; - var targetState = this.stateQueue[index]; - - if (typeof serializedPiskel === "string") { - this.stateQueue[snapshotIndex].piskel = JSON.parse(serializedPiskel); - serializedPiskel = this.stateQueue[snapshotIndex].piskel; - } - - this.loadPiskel(serializedPiskel, this.onPiskelLoadedCallback.bind(this, index, snapshotIndex)); + return index; }; ns.HistoryService.prototype.onPiskelLoadedCallback = function (index, snapshotIndex, piskel) { @@ -89,7 +88,7 @@ var lastState = this.stateQueue[index]; this.setupState(lastState); - + this.currentIndex = index; $.publish(Events.PISKEL_RESET); }; @@ -108,33 +107,11 @@ }; ns.HistoryService.prototype.replayState = function (state) { - var type = state.action.type; - if (type === 'DELETE_FRAME') { - this.piskelController.removeFrameAt(state.action.index); - } else if (type === 'ADD_FRAME') { - this.piskelController.addFrameAt(state.action.index); - } else if (type === 'DUPLICATE_FRAME') { - this.piskelController.duplicateFrameAt(state.action.index); - } else if (type === 'CREATE_LAYER') { - this.piskelController.createLayer(state.action.name); - } else if (type === 'REMOVE_LAYER') { - this.piskelController.removeCurrentLayer(); - } else if (type === 'LAYER_UP') { - this.piskelController.moveLayerUp(); - } else if (type === 'LAYER_DOWN') { - this.piskelController.moveLayerUp(); - } else if (type === 'RENAME_LAYER') { - this.piskelController.renameLayerAt(state.action.index, state.action.name); - } else if (type === 'MOVE_FRAME') { - this.piskelController.moveFrame(state.action.from, state.action.to); - } else if (type === 'CREATE_LAYER') { - this.piskelController.createLayer(); - } else if (type === 'TOOL') { - var action = state.action; - var layer = this.piskelController.getLayerAt(state.layerIndex); - var frame = layer.getFrameAt(state.frameIndex); - action.tool.replay(frame, action.replay); - } + var action = state.action; + var type = action.type; + var layer = this.piskelController.getLayerAt(state.layerIndex); + var frame = layer.getFrameAt(state.frameIndex); + action.scope.replay(frame, action.replay); }; })(); \ No newline at end of file diff --git a/src/js/utils/core.js b/src/js/utils/core.js index c37a7033..60e95e35 100644 --- a/src/js/utils/core.js +++ b/src/js/utils/core.js @@ -47,5 +47,13 @@ if (typeof Function.prototype.bind !== "function") { extendedObject.prototype.superclass = inheritFrom.prototype; }; + ns.wrap = function (wrapper, wrappedObject) { + for (var prop in wrappedObject) { + if (typeof wrappedObject[prop] === 'function') { + wrapper[prop] = wrappedObject[prop].bind(wrappedObject); + } + } + }; + })();