First implementation of tool tester

This commit is contained in:
jdescottes 2014-08-21 00:50:59 +02:00
parent 34dbcedec3
commit 1955d3f8f5
12 changed files with 422 additions and 30 deletions

View File

@ -2,6 +2,8 @@
var Events = {
TOOL_SELECTED : "TOOL_SELECTED",
SELECT_TOOL : "SELECT_TOOL",
TOOL_RELEASED : "TOOL_RELEASED",
SELECT_PRIMARY_COLOR: "SELECT_PRIMARY_COLOR",
SELECT_SECONDARY_COLOR: "SELECT_SECONDARY_COLOR",
@ -47,5 +49,11 @@ var Events = {
ZOOM_CHANGED : "ZOOM_CHANGED",
CURRENT_COLORS_UPDATED : "CURRENT_COLORS_UPDATED"
CURRENT_COLORS_UPDATED : "CURRENT_COLORS_UPDATED",
MOUSE_EVENT : "MOUSE_EVENT",
// Tests
TEST_RECORD_END : "TEST_RECORD_END"
};

View File

@ -29,8 +29,8 @@
var layer = new pskl.model.Layer("Layer 1");
var frame = new pskl.model.Frame(size.width, size.height);
layer.addFrame(frame);
layer.addFrame(frame);
piskel.addLayer(layer);
this.corePiskelController = new pskl.controller.piskel.PiskelController(piskel);
@ -115,6 +115,15 @@
}
this.storageService.init();
// test tools
var testModeOn = document.location.href.toLowerCase().indexOf('test=true') !== -1;
if (testModeOn) {
this.testRecorder = new pskl.devtools.TestRecorder(this.piskelController);
this.testRecorder.init();
this.testRecordController = new pskl.devtools.TestRecordController(this.testRecorder);
this.testRecordController.init();
}
var drawingLoop = new pskl.rendering.DrawingLoop();
drawingLoop.addCallback(this.render, this);

View File

@ -134,8 +134,9 @@
* @private
*/
ns.DrawingController.prototype.onMousedown_ = function (event) {
$.publish(Events.MOUSE_EVENT, [event, this]);
var frame = this.piskelController.getCurrentFrame();
var coords = this.renderer.getCoordinates(event.clientX, event.clientY);
var coords = this.getSpriteCoordinates(event.clientX, event.clientY);
if (event.button === Constants.MIDDLE_BUTTON) {
if (frame.containsPixel(coords.x, coords.y)) {
@ -204,10 +205,11 @@
};
ns.DrawingController.prototype.moveTool_ = function (x, y, event) {
var coords = this.renderer.getCoordinates(x, y);
var coords = this.getSpriteCoordinates(x, y);
var currentFrame = this.piskelController.getCurrentFrame();
if (this.isClicked) {
$.publish(Events.MOUSE_EVENT, [event, this]);
// Warning : do not call setCurrentButton here
// mousemove do not have the correct mouse button information on all browsers
this.currentToolBehavior.moveToolAt(
@ -248,6 +250,7 @@
*/
ns.DrawingController.prototype.onMouseup_ = function (event) {
if(this.isClicked) {
$.publish(Events.MOUSE_EVENT, [event, this]);
// A mouse button was clicked on the drawing canvas before this mouseup event,
// the user was probably drawing on the canvas.
// Note: The mousemove movement (and the mouseup) may end up outside
@ -256,7 +259,7 @@
this.isClicked = false;
this.setCurrentButton(event);
var coords = this.renderer.getCoordinates(event.clientX, event.clientY);
var coords = this.getSpriteCoordinates(event.clientX, event.clientY);
this.currentToolBehavior.releaseToolAt(
coords.x,
coords.y,
@ -280,6 +283,10 @@
return this.renderer.getCoordinates(screenX, screenY);
};
ns.DrawingController.prototype.getScreenCoordinates = function(spriteX, spriteY) {
return this.renderer.reverseCoordinates(spriteX, spriteY);
};
ns.DrawingController.prototype.setCurrentButton = function (event) {
this.currentMouseButton_ = event.button;
};

View File

@ -38,6 +38,8 @@
this.selectTool_(this.tools[0]);
// Activate listener on tool panel:
$("#tool-section").mousedown($.proxy(this.onToolIconClicked_, this));
$.subscribe(Events.SELECT_TOOL, this.onSelectToolEvent_.bind(this));
};
/**
@ -54,6 +56,13 @@
stage.data("selected-tool-class", tool.instance.toolId);
};
ns.ToolController.prototype.onSelectToolEvent_ = function(event, toolId) {
var tool = this.getToolById_(toolId);
if (tool) {
this.selectTool_(tool);
}
};
/**
* @private
*/

View File

@ -47,17 +47,6 @@
this.duplicateFrameAt(this.getCurrentFrameIndex());
};
ns.PublicPiskelController.prototype.raiseSaveStateEvent_ = function (fn, args) {
$.publish(Events.PISKEL_SAVE_STATE, {
type : pskl.service.HistoryService.REPLAY_NO_SNAPSHOT,
scope : this,
replay : {
fn : fn,
args : args
}
});
};
ns.PublicPiskelController.prototype.replay = function (frame, replayData) {
replayData.fn.apply(this.piskelController, replayData.args);
};
@ -141,4 +130,15 @@
return this.piskelController.piskel;
};
ns.PublicPiskelController.prototype.raiseSaveStateEvent_ = function (fn, args) {
$.publish(Events.PISKEL_SAVE_STATE, {
type : pskl.service.HistoryService.REPLAY_NO_SNAPSHOT,
scope : this,
replay : {
fn : fn,
args : args
}
});
};
})();

View File

@ -39,7 +39,10 @@
};
ns.ImportController.prototype.onOpenPiskelChange_ = function (evt) {
this.openPiskelFile_();
var files = this.hiddenOpenPiskelInput.get(0).files;
if (files.length == 1) {
this.openPiskelFile_(files[0]);
}
};
ns.ImportController.prototype.onOpenPiskelClick_ = function (evt) {
@ -51,19 +54,14 @@
this.closeDrawer_();
};
ns.ImportController.prototype.openPiskelFile_ = function () {
var files = this.hiddenOpenPiskelInput.get(0).files;
if (files.length == 1) {
var file = files[0];
if (this.isPiskel_(file)){
pskl.utils.PiskelFileUtils.loadFromFile(file, function (piskel, descriptor, fps) {
piskel.setDescriptor(descriptor);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(fps);
});
this.closeDrawer_();
}
ns.ImportController.prototype.openPiskelFile_ = function (file) {
if (this.isPiskel_(file)){
pskl.utils.PiskelFileUtils.loadFromFile(file, function (piskel, descriptor, fps) {
piskel.setDescriptor(descriptor);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(fps);
});
this.closeDrawer_();
}
};

View File

@ -0,0 +1,25 @@
(function () {
var ns = $.namespace('pskl.devtools');
ns.MouseEvent = function (event, coords) {
this.event = {
type : event.type,
button : event.button,
shiftKey : event.shiftKey,
altKey : event.altKey,
ctrlKey : event.ctrlKey
};
this.coords = coords;
this.type = 'mouse-event';
};
ns.MouseEvent.prototype.equals = function (otherEvent) {
if (otherEvent && otherEvent instanceof ns.MouseEvent) {
var sameEvent = JSON.stringify(otherEvent.event) == JSON.stringify(this.event);
var sameCoords = JSON.stringify(otherEvent.coords) == JSON.stringify(this.coords);
} else {
return false;
}
};
})();

View File

@ -0,0 +1,76 @@
(function () {
var ns = $.namespace('pskl.devtools');
ns.TestRecordController = function (testRecorder) {
this.testRecorder = testRecorder;
$.subscribe(Events.TEST_RECORD_END, this.onTestRecordEnd_.bind(this));
};
ns.TestRecordController.prototype.init = function () {
var fileInput = document.createElement('input');
fileInput.setAttribute('type', 'file');
fileInput.addEventListener('change', this.onFileInputChange_.bind(this));
fileInput.style.display = 'none';
var container = document.createElement('div');
container.style.cssText = 'position:absolute;z-index:10000;margin:5px;padding:10px;background:lightgrey';
document.body.appendChild(container);
var loadInput = document.createElement('button');
loadInput.innerHTML = 'Load Test ...';
loadInput.addEventListener('click', this.onLoadInputClick_.bind(this));
var startInput = document.createElement('button');
startInput.innerHTML = 'Start record';
startInput.addEventListener('click', this.onStartInputClick_.bind(this));
var stopInput = document.createElement('button');
stopInput.innerHTML = 'Stop record';
stopInput.addEventListener('click', this.onStopInputClick_.bind(this));
stopInput.setAttribute('disabled','disabled');
this.container = container;
this.fileInput = this.container.appendChild(fileInput);
this.loadInput = this.container.appendChild(loadInput);
this.startInput = this.container.appendChild(startInput);
this.stopInput = this.container.appendChild(stopInput);
};
ns.TestRecordController.prototype.onLoadInputClick_ = function () {
this.fileInput.click();
};
ns.TestRecordController.prototype.onFileInputChange_ = function () {
var files = this.fileInput.files;
if (files.length == 1) {
var file =files[0];
pskl.utils.FileUtils.readFile(file, function (content) {
var testRecord = JSON.parse(window.atob(content.replace(/data\:.*?\;base64\,/,'')));
var testRecordPlayer = new ns.TestRecordPlayer(testRecord);
testRecordPlayer.start();
}.bind(this));
}
};
ns.TestRecordController.prototype.onStartInputClick_ = function () {
this.testRecorder.startRecord();
this.startInput.setAttribute('disabled','disabled');
this.stopInput.removeAttribute('disabled');
};
ns.TestRecordController.prototype.onStopInputClick_ = function () {
var testRecord = this.testRecorder.stopRecord();
pskl.utils.BlobUtils.stringToBlob(testRecord, function(blob) {
pskl.utils.FileUtils.downloadAsFile(blob, 'record_piskel.json');
}.bind(this), "application/json");
this.startInput.removeAttribute('disabled');
this.stopInput.setAttribute('disabled','disabled');
};
ns.TestRecordController.prototype.onTestRecordEnd_ = function (evt, success) {
console.log('test finished : ', success);
};
})();

View File

@ -0,0 +1,116 @@
(function () {
var ns = $.namespace('pskl.devtools');
ns.TestRecordPlayer = function (testRecord) {
this.initialState = testRecord.initialState;
this.events = testRecord.events;
this.png = testRecord.png;
this.shim = null;
};
ns.TestRecordPlayer.STEP = 30;
ns.TestRecordPlayer.prototype.start = function () {
this.setupInitialState_();
this.createMouseShim_();
this.playEvent_(0);
};
ns.TestRecordPlayer.prototype.setupInitialState_ = function () {
var size = this.initialState.size;
var piskel = this.createPiskel_(size.width, size.height);
pskl.app.piskelController.setPiskel(piskel, true);
$.publish(Events.SELECT_PRIMARY_COLOR, [this.initialState.primaryColor]);
$.publish(Events.SELECT_SECONDARY_COLOR, [this.initialState.secondaryColor]);
$.publish(Events.SELECT_TOOL, [this.initialState.selectedTool]);
};
ns.TestRecordPlayer.prototype.createMouseShim_ = function () {
this.shim = document.createElement('DIV');
this.shim.style.cssText = 'position:fixed;top:0;left:0;right:0;left:0;bottom:0;z-index:15000';
this.shim.addEventListener('mousemove', function (e) {
e.stopPropagation();
e.preventDefault();
});
document.body.appendChild(this.shim);
};
ns.TestRecordPlayer.prototype.createPiskel_ = function (width, height) {
var descriptor = new pskl.model.piskel.Descriptor('TestPiskel', '');
var piskel = new pskl.model.Piskel(width, height, descriptor);
var layer = new pskl.model.Layer("Layer 1");
var frame = new pskl.model.Frame(width, height);
layer.addFrame(frame);
piskel.addLayer(layer);
return piskel;
};
ns.TestRecordPlayer.prototype.playEvent_ = function (index) {
this.timer = window.setTimeout(function () {
var recordEvent = this.events[index];
if (recordEvent.type === 'mouse-event') {
this.playMouseEvent_(recordEvent);
} else if (recordEvent.type === 'color-event') {
this.playColorEvent_(recordEvent);
} else if (recordEvent.type === 'tool-event') {
this.playToolEvent_(recordEvent);
} else if (recordEvent.type === 'instrumented-event') {
this.playInstrumentedEvent_(recordEvent);
}
if (this.events[index+1]) {
this.playEvent_(index+1);
} else {
this.onTestEnd_();
}
}.bind(this), ns.TestRecordPlayer.STEP);
};
ns.TestRecordPlayer.prototype.onTestEnd_ = function () {
var renderer = new pskl.rendering.PiskelRenderer(pskl.app.piskelController);
var png = renderer.renderAsCanvas().toDataURL();
var success = png === this.png;
this.shim.parentNode.removeChild(this.shim);
this.shim = null;
$.publish(Events.TEST_RECORD_END, [success]);
};
ns.TestRecordPlayer.prototype.playMouseEvent_ = function (recordEvent) {
var event = recordEvent.event;
var screenCoordinates = pskl.app.drawingController.getScreenCoordinates(recordEvent.coords.x, recordEvent.coords.y);
event.clientX = screenCoordinates.x;
event.clientY = screenCoordinates.y;
if (event.type == 'mousedown') {
pskl.app.drawingController.onMousedown_(event);
} else if (event.type == 'mouseup') {
pskl.app.drawingController.onMouseup_(event);
} else if (event.type == 'mousemove') {
pskl.app.drawingController.onMousemove_(event);
}
};
ns.TestRecordPlayer.prototype.playColorEvent_ = function (recordEvent) {
if (recordEvent.isPrimary) {
$.publish(Events.SELECT_PRIMARY_COLOR, [recordEvent.color]);
} else {
$.publish(Events.SELECT_SECONDARY_COLOR, [recordEvent.color]);
}
};
ns.TestRecordPlayer.prototype.playToolEvent_ = function (recordEvent) {
$.publish(Events.SELECT_TOOL, [recordEvent.toolId]);
};
ns.TestRecordPlayer.prototype.playInstrumentedEvent_ = function (recordEvent) {
pskl.app.piskelController[recordEvent.methodName].apply(pskl.app.piskelController, recordEvent.args);
};
})();

View File

@ -0,0 +1,115 @@
(function () {
var ns = $.namespace('pskl.devtools');
ns.TestRecorder = function (piskelController) {
this.piskelController = piskelController;
this.isRecording = false;
this.reset();
};
ns.TestRecorder.prototype.init = function () {
$.subscribe(Events.MOUSE_EVENT, this.onMouseEvent_.bind(this));
$.subscribe(Events.TOOL_SELECTED, this.onToolEvent_.bind(this));
$.subscribe(Events.PRIMARY_COLOR_SELECTED, this.onColorEvent_.bind(this, true));
$.subscribe(Events.SECONDARY_COLOR_SELECTED, this.onColorEvent_.bind(this, false));
for (var key in this.piskelController) {
if (typeof this.piskelController[key] == 'function') {
var methodTriggersReset = this.piskelController[key].toString().indexOf('Events.PISKEL_RESET') != -1;
if (methodTriggersReset) {
this.piskelController[key] = this.instrumentMethod_(this.piskelController, key);
}
}
}
};
ns.TestRecorder.prototype.instrumentMethod_ = function (object, methodName) {
var method = object[methodName];
var testRecorder = this;
return function () {
testRecorder.onInstrumentedMethod_(object, methodName, arguments);
return method.apply(this, arguments);
};
};
ns.TestRecorder.prototype.reset = function () {
this.initialState = {};
this.events = [];
};
ns.TestRecorder.prototype.startRecord = function () {
this.isRecording = true;
this.initialState = {
size : {
width : this.piskelController.getWidth(),
height : this.piskelController.getHeight()
},
primaryColor : pskl.app.paletteController.getPrimaryColor(),
secondaryColor : pskl.app.paletteController.getSecondaryColor(),
selectedTool : pskl.app.toolController.currentSelectedTool.instance.toolId
};
};
ns.TestRecorder.prototype.stopRecord = function () {
this.isRecording = false;
var renderer = new pskl.rendering.PiskelRenderer(this.piskelController);
var png = renderer.renderAsCanvas().toDataURL();
var testRecord = JSON.stringify({
events : this.events,
initialState : this.initialState,
png : png
});
this.reset();
return testRecord;
};
ns.TestRecorder.prototype.onMouseEvent_ = function (evt, mouseEvent, originator) {
if (this.isRecording) {
this.recordMouseEvent_(mouseEvent);
}
};
ns.TestRecorder.prototype.onColorEvent_ = function (isPrimary, evt, color) {
if (this.isRecording) {
var recordEvent = {};
recordEvent.type = 'color-event';
recordEvent.color = color;
recordEvent.isPrimary = isPrimary;
this.events.push(recordEvent);
}
};
ns.TestRecorder.prototype.onToolEvent_ = function (evt, tool) {
if (this.isRecording) {
var recordEvent = {};
recordEvent.type = 'tool-event';
recordEvent.toolId = tool.toolId;
this.events.push(recordEvent);
}
};
ns.TestRecorder.prototype.onInstrumentedMethod_ = function (callee, methodName, args) {
if (this.isRecording) {
var recordEvent = {};
recordEvent.type = 'instrumented-event';
recordEvent.methodName = methodName;
recordEvent.args = Array.prototype.slice.call(args, 0);
this.events.push(recordEvent);
}
};
ns.TestRecorder.prototype.recordMouseEvent_ = function (mouseEvent) {
var coords = pskl.app.drawingController.getSpriteCoordinates(mouseEvent.clientX, mouseEvent.clientY);
var recordEvent = new ns.MouseEvent(mouseEvent, coords);
var lastEvent = this.events[this.events.length-1];
if (!recordEvent.equals(lastEvent)) {
this.events.push(recordEvent);
}
};
})();

View File

@ -196,6 +196,28 @@
};
};
ns.FrameRenderer.prototype.reverseCoordinates = function(x, y) {
var cellSize = this.zoom;
x = x * cellSize;
y = y * cellSize;
x = x - this.offset.x * cellSize;
y = y - this.offset.y * cellSize;
x = x + this.margin.x;
y = y + this.margin.y;
var containerOffset = this.container.offset();
x = x + containerOffset.left;
y = y + containerOffset.top;
return {
x : x + (cellSize/2),
y : y + (cellSize/2)
};
};
/**
* @private
*/

View File

@ -137,6 +137,13 @@
"js/drawingtools/selectiontools/ShapeSelect.js",
"js/drawingtools/ColorPicker.js",
"js/drawingtools/ColorSwap.js",
// Devtools
"js/devtools/MouseEvent.js",
"js/devtools/TestRecorder.js",
"js/devtools/TestRecordPlayer.js",
"js/devtools/TestRecordController.js",
// Application controller and initialization
"js/app.js",
// Bonus features !!