Seperated desktop and mobile input controllers, changed desktop drag mode to be ctrl/meta triggered instread
This commit is contained in:
parent
2b8368d63b
commit
b977b8c174
|
@ -2,8 +2,9 @@ java -client -jar closure-compiler.jar \
|
||||||
--js js-lib/common.js \
|
--js js-lib/common.js \
|
||||||
--js js-lib/view.js \
|
--js js-lib/view.js \
|
||||||
--js js-lib/draw.js \
|
--js js-lib/draw.js \
|
||||||
--js js-lib/controller.js \
|
|
||||||
--js js-lib/state.js \
|
--js js-lib/state.js \
|
||||||
|
--js js-lib/controller.js \
|
||||||
|
--js js-lib/input-controller.js \
|
||||||
--js js-lib/launch.js \
|
--js js-lib/launch.js \
|
||||||
--warning_level=VERBOSE --formatting=PRETTY_PRINT --language_in=ECMASCRIPT5 --compilation_level=ADVANCED_OPTIMIZATIONS \
|
--warning_level=VERBOSE --formatting=PRETTY_PRINT --language_in=ECMASCRIPT5 --compilation_level=ADVANCED_OPTIMIZATIONS \
|
||||||
--externs=jquery-1.9-externs.js \
|
--externs=jquery-1.9-externs.js \
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
/**
|
||||||
|
* Different modes of control.
|
||||||
|
* @const
|
||||||
|
*/
|
||||||
|
var Mode = {
|
||||||
|
NONE : 0,
|
||||||
|
DRAG : 1,
|
||||||
|
DRAW : 2
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles user input events and modifies state.
|
* Handles user input events and modifies state.
|
||||||
*
|
*
|
||||||
|
@ -12,10 +22,9 @@ ascii.Controller = function(view, state) {
|
||||||
/** @type {ascii.DrawFunction} */ this.drawFunction =
|
/** @type {ascii.DrawFunction} */ this.drawFunction =
|
||||||
new ascii.DrawBox(state);
|
new ascii.DrawBox(state);
|
||||||
|
|
||||||
|
/** @type {number} */ this.mode = Mode.NONE;
|
||||||
/** @type {ascii.Vector} */ this.dragOrigin;
|
/** @type {ascii.Vector} */ this.dragOrigin;
|
||||||
/** @type {ascii.Vector} */ this.pressVector;
|
/** @type {ascii.Vector} */ this.dragOriginCell;
|
||||||
/** @type {ascii.Vector} */ this.lastMoveCell;
|
|
||||||
/** @type {number} */ this.pressTimestamp;
|
|
||||||
|
|
||||||
this.installBindings();
|
this.installBindings();
|
||||||
};
|
};
|
||||||
|
@ -23,68 +32,59 @@ ascii.Controller = function(view, state) {
|
||||||
/**
|
/**
|
||||||
* @param {ascii.Vector} position
|
* @param {ascii.Vector} position
|
||||||
*/
|
*/
|
||||||
ascii.Controller.prototype.handlePress = function(position) {
|
ascii.Controller.prototype.startDraw = function(position) {
|
||||||
this.pressVector = position;
|
this.mode = Mode.DRAW;
|
||||||
this.pressTimestamp = $.now();
|
this.drawFunction.start(this.view.screenToCell(position));
|
||||||
|
}
|
||||||
|
|
||||||
// Check to see if a drag happened in the given allowed time.
|
/**
|
||||||
window.setTimeout(function() {
|
* @param {ascii.Vector} position
|
||||||
if (this.dragOrigin == null && this.pressVector != null) {
|
*/
|
||||||
this.drawFunction.start(this.view.screenToCell(position));
|
ascii.Controller.prototype.startDrag = function(position) {
|
||||||
}
|
this.mode = Mode.DRAG;
|
||||||
// TODO: Skip this if release happens before timeout.
|
this.dragOrigin = position;
|
||||||
}.bind(this), DRAG_LATENCY);
|
this.dragOriginCell = this.view.offset;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {ascii.Vector} position
|
* @param {ascii.Vector} position
|
||||||
*/
|
*/
|
||||||
ascii.Controller.prototype.handleMove = function(position) {
|
ascii.Controller.prototype.handleMove = function(position) {
|
||||||
// Update the cursor pointer, depending on the draw function.
|
var moveCell = this.view.screenToCell(position);
|
||||||
this.view.canvas.style.cursor = this.drawFunction.getCursor(
|
|
||||||
this.view.screenToCell(position));
|
|
||||||
|
|
||||||
// No clicks, so just ignore.
|
// First move event, make sure we don't blow up here.
|
||||||
if (this.pressVector == null) { return; }
|
if (this.lastMoveCell == null) {
|
||||||
|
this.lastMoveCell = moveCell;
|
||||||
// Initiate a drag if we have moved enough, quickly enough.
|
|
||||||
if (this.dragOrigin == null &&
|
|
||||||
($.now() - this.pressTimestamp) < DRAG_LATENCY &&
|
|
||||||
position.subtract(this.pressVector).length() > DRAG_ACCURACY) {
|
|
||||||
this.dragOrigin = this.view.offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not dragging, so pass the mouse move on, but remove duplicates.
|
// Update the cursor pointer, depending on the draw function.
|
||||||
if (this.dragOrigin == null &&
|
if (!moveCell.equals(this.lastMoveCell)) {
|
||||||
($.now() - this.pressTimestamp) >= DRAG_LATENCY &&
|
this.view.canvas.style.cursor = this.drawFunction.getCursor(moveCell);
|
||||||
(this.lastMoveCell == null ||
|
}
|
||||||
!this.view.screenToCell(position)
|
|
||||||
.equals(this.view.screenToCell(this.lastMoveCell)))) {
|
// In drawing mode, so pass the mouse move on, but remove duplicates.
|
||||||
this.drawFunction.move(this.view.screenToCell(position));
|
if (this.mode == Mode.DRAW && !moveCell.equals(this.lastMoveCell)) {
|
||||||
this.lastMoveCell = position;
|
this.drawFunction.move(moveCell);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drag in progress, update the view origin.
|
// Drag in progress, update the view origin.
|
||||||
if (this.dragOrigin != null) {
|
if (this.mode == Mode.DRAG) {
|
||||||
this.view.setOffset(this.dragOrigin.add(
|
this.view.setOffset(this.dragOriginCell.add(
|
||||||
this.pressVector
|
this.dragOrigin
|
||||||
.subtract(position)
|
.subtract(position)
|
||||||
.scale(1 / this.view.zoom)));
|
.scale(1 / this.view.zoom)));
|
||||||
}
|
}
|
||||||
|
this.lastMoveCell = moveCell;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
ascii.Controller.prototype.endAll = function() {
|
||||||
* @param {ascii.Vector} position
|
if (this.mode = Mode.DRAW) {
|
||||||
*/
|
this.drawFunction.end();
|
||||||
ascii.Controller.prototype.handleRelease = function(position) {
|
|
||||||
// Drag wasn't initiated in time, treat this as a drawing event.
|
|
||||||
if (this.dragOrigin == null &&
|
|
||||||
($.now() - this.pressTimestamp) >= DRAG_LATENCY) {
|
|
||||||
this.drawFunction.end(this.view.screenToCell(position));
|
|
||||||
}
|
}
|
||||||
this.pressVector = null;
|
// Cleanup state.
|
||||||
this.pressTimestamp = 0;
|
this.mode = Mode.NONE;
|
||||||
this.dragOrigin = null;
|
this.dragOrigin = null;
|
||||||
|
this.dragOriginCell = null;
|
||||||
this.lastMoveCell = null;
|
this.lastMoveCell = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -98,53 +98,13 @@ ascii.Controller.prototype.handleZoom = function(delta) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Installs input bindings for desktop devices.
|
* Installs input bindings for common use cases devices.
|
||||||
*/
|
*/
|
||||||
ascii.Controller.prototype.installBindings = function() {
|
ascii.Controller.prototype.installBindings = function() {
|
||||||
var controller = this;
|
var controller = this;
|
||||||
|
|
||||||
$(this.view.canvas).bind('mousewheel', function(e) {
|
|
||||||
controller.handleZoom(e.originalEvent.wheelDelta);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(this.view.canvas).mousedown(function(e) {
|
|
||||||
controller.handlePress(new ascii.Vector(e.clientX, e.clientY));
|
|
||||||
});
|
|
||||||
|
|
||||||
$(this.view.canvas).mouseup(function(e) {
|
|
||||||
controller.handleRelease(new ascii.Vector(e.clientX, e.clientY));
|
|
||||||
});
|
|
||||||
|
|
||||||
$(this.view.canvas).mouseleave(function(e) {
|
|
||||||
controller.handleRelease(new ascii.Vector(e.clientX, e.clientY));
|
|
||||||
});
|
|
||||||
|
|
||||||
$(this.view.canvas).mousemove(function(e) {
|
|
||||||
controller.handleMove(new ascii.Vector(e.clientX, e.clientY));
|
|
||||||
});
|
|
||||||
|
|
||||||
$(window).resize(function(e) { controller.view.resizeCanvas() });
|
$(window).resize(function(e) { controller.view.resizeCanvas() });
|
||||||
|
|
||||||
$(this.view.canvas).bind('touchstart', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
controller.handlePress(new ascii.Vector(
|
|
||||||
e.originalEvent.touches[0].pageX,
|
|
||||||
e.originalEvent.touches[0].pageY));
|
|
||||||
});
|
|
||||||
|
|
||||||
$(this.view.canvas).bind('touchend', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
// TODO: This works for now as we don't use a touchend position anywhere.
|
|
||||||
// Need to track last position from touchmove and use it here.
|
|
||||||
controller.handleRelease(new ascii.Vector(0, 0));
|
|
||||||
});
|
|
||||||
|
|
||||||
$(this.view.canvas).bind('touchmove', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
controller.handleMove(new ascii.Vector(
|
|
||||||
e.originalEvent.touches[0].pageX,
|
|
||||||
e.originalEvent.touches[0].pageY));
|
|
||||||
});
|
|
||||||
// TODO: Handle pinch to zoom.
|
// TODO: Handle pinch to zoom.
|
||||||
|
|
||||||
$('#buttons > button.tool').click(function(e) {
|
$('#buttons > button.tool').click(function(e) {
|
||||||
|
|
|
@ -53,8 +53,8 @@ ascii.DrawFunction = function() {};
|
||||||
ascii.DrawFunction.prototype.start = function(position) {};
|
ascii.DrawFunction.prototype.start = function(position) {};
|
||||||
/** Drawing move. @param {ascii.Vector} position */
|
/** Drawing move. @param {ascii.Vector} position */
|
||||||
ascii.DrawFunction.prototype.move = function(position) {};
|
ascii.DrawFunction.prototype.move = function(position) {};
|
||||||
/** End of drawing. @param {ascii.Vector} position */
|
/** End of drawing. */
|
||||||
ascii.DrawFunction.prototype.end = function(position) {};
|
ascii.DrawFunction.prototype.end = function() {};
|
||||||
/** Cursor for given cell.
|
/** Cursor for given cell.
|
||||||
* @param {ascii.Vector} position
|
* @param {ascii.Vector} position
|
||||||
* @return {string}
|
* @return {string}
|
||||||
|
@ -82,7 +82,7 @@ ascii.DrawBox.prototype.move = function(position) {
|
||||||
drawLine(this.state, this.startPosition, position, true);
|
drawLine(this.state, this.startPosition, position, true);
|
||||||
drawLine(this.state, this.startPosition, position, false);
|
drawLine(this.state, this.startPosition, position, false);
|
||||||
};
|
};
|
||||||
ascii.DrawBox.prototype.end = function(position) {
|
ascii.DrawBox.prototype.end = function() {
|
||||||
this.state.commitDraw();
|
this.state.commitDraw();
|
||||||
};
|
};
|
||||||
ascii.DrawBox.prototype.getCursor = function(position) {
|
ascii.DrawBox.prototype.getCursor = function(position) {
|
||||||
|
@ -115,7 +115,7 @@ ascii.DrawLine.prototype.move = function(position) {
|
||||||
|
|
||||||
drawLine(this.state, this.startPosition, position, clockwise);
|
drawLine(this.state, this.startPosition, position, clockwise);
|
||||||
};
|
};
|
||||||
ascii.DrawLine.prototype.end = function(position) {
|
ascii.DrawLine.prototype.end = function() {
|
||||||
this.state.commitDraw();
|
this.state.commitDraw();
|
||||||
};
|
};
|
||||||
ascii.DrawLine.prototype.getCursor = function(position) {
|
ascii.DrawLine.prototype.getCursor = function(position) {
|
||||||
|
@ -140,7 +140,7 @@ ascii.DrawFreeform.prototype.start = function(position) {
|
||||||
ascii.DrawFreeform.prototype.move = function(position) {
|
ascii.DrawFreeform.prototype.move = function(position) {
|
||||||
this.state.drawValue(position, this.value);
|
this.state.drawValue(position, this.value);
|
||||||
};
|
};
|
||||||
ascii.DrawFreeform.prototype.end = function(position) {
|
ascii.DrawFreeform.prototype.end = function() {
|
||||||
this.state.commitDraw();
|
this.state.commitDraw();
|
||||||
};
|
};
|
||||||
ascii.DrawFreeform.prototype.getCursor = function(position) {
|
ascii.DrawFreeform.prototype.getCursor = function(position) {
|
||||||
|
@ -174,7 +174,7 @@ ascii.DrawText.prototype.start = function(position) {
|
||||||
this.state.drawValue(position, currentValue == null ? ERASE_CHAR : currentValue);
|
this.state.drawValue(position, currentValue == null ? ERASE_CHAR : currentValue);
|
||||||
};
|
};
|
||||||
ascii.DrawText.prototype.move = function(position) {};
|
ascii.DrawText.prototype.move = function(position) {};
|
||||||
ascii.DrawText.prototype.end = function(position) {};
|
ascii.DrawText.prototype.end = function() {};
|
||||||
ascii.DrawText.prototype.getCursor = function(position) {
|
ascii.DrawText.prototype.getCursor = function(position) {
|
||||||
return 'text';
|
return 'text';
|
||||||
};
|
};
|
||||||
|
@ -259,7 +259,7 @@ ascii.DrawErase.prototype.move = function(position) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ascii.DrawErase.prototype.end = function(position) {
|
ascii.DrawErase.prototype.end = function() {
|
||||||
this.state.commitDraw();
|
this.state.commitDraw();
|
||||||
};
|
};
|
||||||
ascii.DrawErase.prototype.getCursor = function(position) {
|
ascii.DrawErase.prototype.getCursor = function(position) {
|
||||||
|
@ -340,7 +340,7 @@ ascii.DrawMove.prototype.move = function(position) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ascii.DrawMove.prototype.end = function(position) {
|
ascii.DrawMove.prototype.end = function() {
|
||||||
this.state.commitDraw();
|
this.state.commitDraw();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
/**
|
||||||
|
* Handles desktop inputs, and passes them onto the main controller.
|
||||||
|
* @constructor
|
||||||
|
* @param {ascii.Controller} controller
|
||||||
|
*/
|
||||||
|
ascii.DesktopController = function(controller) {
|
||||||
|
/** @type {ascii.Controller} */ this.controller = controller;
|
||||||
|
|
||||||
|
/** @type {boolean} */ this.isDragging = false;
|
||||||
|
|
||||||
|
this.installBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ascii.Vector} position
|
||||||
|
*/
|
||||||
|
ascii.DesktopController.prototype.handlePress = function(position, e) {
|
||||||
|
// Can drag by holding either the control or meta (Apple) key.
|
||||||
|
if (e.ctrlKey || e.metaKey) {
|
||||||
|
this.controller.startDrag(position);
|
||||||
|
} else {
|
||||||
|
this.controller.startDraw(position);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ascii.DesktopController.prototype.installBindings = function() {
|
||||||
|
var canvas = this.controller.view.canvas;
|
||||||
|
$(canvas).bind('mousewheel', function(e) {
|
||||||
|
this.controller.handleZoom(e.originalEvent.wheelDelta);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
$(canvas).mousedown(function(e) {
|
||||||
|
this.handlePress(new ascii.Vector(e.clientX, e.clientY), e);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
// Pass these events through to the main controller.
|
||||||
|
$(canvas).mouseup(function(e) {
|
||||||
|
this.controller.endAll();
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
$(canvas).mouseleave(function(e) {
|
||||||
|
this.controller.endAll();
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
$(canvas).mousemove(function(e) {
|
||||||
|
this.controller.handleMove(new ascii.Vector(e.clientX, e.clientY));
|
||||||
|
}.bind(this));
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles touch inputs, and passes them onto the main controller.
|
||||||
|
* @constructor
|
||||||
|
* @param {ascii.Controller} controller
|
||||||
|
*/
|
||||||
|
ascii.TouchController = function(controller) {
|
||||||
|
/** @type {ascii.Controller} */ this.controller = controller;
|
||||||
|
|
||||||
|
/** @type {ascii.Vector} */ this.pressVector;
|
||||||
|
/** @type {number} */ this.pressTimestamp;
|
||||||
|
/** @type {boolean} */ this.dragStarted = false;
|
||||||
|
|
||||||
|
this.installBindings();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ascii.Vector} position
|
||||||
|
*/
|
||||||
|
ascii.TouchController.prototype.handlePress = function(position) {
|
||||||
|
this.pressVector = position;
|
||||||
|
this.pressTimestamp = $.now();
|
||||||
|
this.dragStarted = false;
|
||||||
|
|
||||||
|
// If a drag didn't start, then handle it as a draw.
|
||||||
|
window.setTimeout(function() {
|
||||||
|
if (!this.dragStarted) {
|
||||||
|
this.controller.startDraw(position);
|
||||||
|
}
|
||||||
|
}.bind(this), DRAG_LATENCY);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ascii.Vector} position
|
||||||
|
*/
|
||||||
|
ascii.TouchController.prototype.handleMove = function(position) {
|
||||||
|
// Initiate a drag if we have moved enough, quickly enough.
|
||||||
|
if (!this.dragStarted &&
|
||||||
|
($.now() - this.pressTimestamp) < DRAG_LATENCY &&
|
||||||
|
position.subtract(this.pressVector).length() > DRAG_ACCURACY) {
|
||||||
|
this.dragStarted = true;
|
||||||
|
this.controller.startDrag(position);
|
||||||
|
}
|
||||||
|
// Pass on the event.
|
||||||
|
this.controller.handleMove(position);
|
||||||
|
};
|
||||||
|
|
||||||
|
ascii.TouchController.prototype.installBindings = function() {
|
||||||
|
var canvas = this.controller.view.canvas;
|
||||||
|
|
||||||
|
$(canvas).bind('touchstart', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.handlePress(new ascii.Vector(
|
||||||
|
e.originalEvent.touches[0].pageX,
|
||||||
|
e.originalEvent.touches[0].pageY));
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
$(canvas).bind('touchmove', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.handleMove(new ascii.Vector(
|
||||||
|
e.originalEvent.touches[0].pageX,
|
||||||
|
e.originalEvent.touches[0].pageY));
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
// Pass through, no special handling.
|
||||||
|
$(canvas).bind('touchend', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.controller.endAll();
|
||||||
|
}.bind(this));
|
||||||
|
};
|
|
@ -5,6 +5,8 @@ ascii.launch = function() {
|
||||||
var state = new ascii.State();
|
var state = new ascii.State();
|
||||||
var view = new ascii.View(state);
|
var view = new ascii.View(state);
|
||||||
var controller = new ascii.Controller(view, state);
|
var controller = new ascii.Controller(view, state);
|
||||||
|
var touchController = new ascii.TouchController(controller);
|
||||||
|
var desktopController = new ascii.DesktopController(controller);
|
||||||
view.animate();
|
view.animate();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue