291 lines
7.2 KiB
JavaScript
291 lines
7.2 KiB
JavaScript
import * as c from "./constants";
|
|
import Vector from "./vector";
|
|
import View from "./view";
|
|
import State from "./state";
|
|
import { DrawFunction, DrawBox, DrawLine, DrawFreeform, DrawErase, DrawMove, DrawText, DrawSelect } from "./draw/index";
|
|
import DrawFunction from "./draw/function";
|
|
|
|
/**
|
|
* Different modes of control.
|
|
*/
|
|
const Mode = {
|
|
NONE: 0,
|
|
DRAG: 1,
|
|
DRAW: 2
|
|
};
|
|
|
|
/**
|
|
* Handles user input events and modifies state.
|
|
*/
|
|
export default class Controller {
|
|
/**
|
|
* @param {View} view
|
|
* @param {State} state
|
|
*/
|
|
constructor(view, state) {
|
|
/** @type {View} */ this.view = view;
|
|
/** @type {State} */ this.state = state;
|
|
|
|
/** @type {DrawFunction} */ this.drawFunction = new DrawBox(state);
|
|
|
|
/** @type {number} */ this.mode = Mode.NONE;
|
|
/** @type {Vector} */ this.dragOrigin;
|
|
/** @type {Vector} */ this.dragOriginCell;
|
|
|
|
/** @type {Vector} */ this.lastMoveCell = null;
|
|
|
|
this.installBindings();
|
|
}
|
|
|
|
/**
|
|
* @param {Vector} position
|
|
*/
|
|
startDraw(position) {
|
|
this.mode = Mode.DRAW;
|
|
this.drawFunction.start(this.view.screenToCell(position));
|
|
}
|
|
|
|
/**
|
|
* @param {Vector} position
|
|
*/
|
|
startDrag(position) {
|
|
this.mode = Mode.DRAG;
|
|
this.dragOrigin = position;
|
|
this.dragOriginCell = this.view.offset;
|
|
}
|
|
|
|
/**
|
|
* @param {Vector} position
|
|
*/
|
|
handleMove(position) {
|
|
var moveCell = this.view.screenToCell(position);
|
|
|
|
// First move event, make sure we don't blow up here.
|
|
if (this.lastMoveCell == null) {
|
|
this.lastMoveCell = moveCell;
|
|
}
|
|
|
|
// Update the cursor pointer, depending on the draw function.
|
|
if (!moveCell.equals(this.lastMoveCell)) {
|
|
this.view.canvas.style.cursor = this.drawFunction.getCursor(moveCell);
|
|
}
|
|
|
|
// In drawing mode, so pass the mouse move on, but remove duplicates.
|
|
if (this.mode == Mode.DRAW && !moveCell.equals(this.lastMoveCell)) {
|
|
this.drawFunction.move(moveCell);
|
|
}
|
|
|
|
// Drag in progress, update the view origin.
|
|
if (this.mode == Mode.DRAG) {
|
|
this.view.setOffset(this.dragOriginCell.add(this.dragOrigin.subtract(position).scale(1 / this.view.zoom)));
|
|
}
|
|
this.lastMoveCell = moveCell;
|
|
}
|
|
|
|
/**
|
|
* Ends the current operation.
|
|
*/
|
|
endAll() {
|
|
if (this.mode == Mode.DRAW) {
|
|
this.drawFunction.end();
|
|
}
|
|
// Cleanup state.
|
|
this.mode = Mode.NONE;
|
|
this.dragOrigin = null;
|
|
this.dragOriginCell = null;
|
|
this.lastMoveCell = null;
|
|
}
|
|
|
|
/**
|
|
* Installs input bindings for common use cases devices.
|
|
*/
|
|
installBindings() {
|
|
$(window).resize(e => {
|
|
this.view.resizeCanvas();
|
|
});
|
|
|
|
$("#draw-tools > button.tool").click(e => {
|
|
$("#text-tool-widget").hide(0);
|
|
this.handleDrawButton(e.target.id);
|
|
});
|
|
|
|
$("#file-tools > button.tool").click(e => {
|
|
this.handleFileButton(e.target.id);
|
|
});
|
|
|
|
$("button.close-dialog-button").click(e => {
|
|
$(".dialog").removeClass("visible");
|
|
});
|
|
|
|
$("#import-submit-button").click(e => {
|
|
this.state.clear();
|
|
this.state.fromText(
|
|
/** @type {string} */
|
|
($("#import-area").val()),
|
|
this.view.screenToCell(new Vector(this.view.canvas.width / 2, this.view.canvas.height / 2))
|
|
);
|
|
this.state.commitDraw();
|
|
$("#import-area").val("");
|
|
$(".dialog").removeClass("visible");
|
|
});
|
|
|
|
$("#use-lines-button").click(e => {
|
|
$(".dialog").removeClass("visible");
|
|
this.view.setUseLines(true);
|
|
});
|
|
|
|
$("#use-ascii-button").click(e => {
|
|
$(".dialog").removeClass("visible");
|
|
this.view.setUseLines(false);
|
|
});
|
|
|
|
$(window).keypress(e => {
|
|
this.handleKeyPress(e);
|
|
});
|
|
|
|
$(window).keydown(e => {
|
|
this.handleKeyDown(e);
|
|
});
|
|
|
|
// Bit of a hack, just triggers the text tool to get a new value.
|
|
$("#text-tool-input, #freeform-tool-input").keyup(() => {
|
|
this.drawFunction.handleKey("");
|
|
});
|
|
$("#text-tool-input, #freeform-tool-input").change(() => {
|
|
this.drawFunction.handleKey("");
|
|
});
|
|
$("#text-tool-close").click(() => {
|
|
$("#text-tool-widget").hide();
|
|
this.state.commitDraw();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Handles the buttons in the UI.
|
|
* @param {string} id The ID of the element clicked.
|
|
*/
|
|
handleDrawButton(id) {
|
|
$("#draw-tools > button.tool").removeClass("active");
|
|
$("#" + id).toggleClass("active");
|
|
$(".dialog").removeClass("visible");
|
|
|
|
// Install the right draw tool based on button pressed.
|
|
if (id == "box-button") {
|
|
this.drawFunction = new DrawBox(this.state);
|
|
}
|
|
if (id == "line-button") {
|
|
this.drawFunction = new DrawLine(this.state, false);
|
|
}
|
|
if (id == "arrow-button") {
|
|
this.drawFunction = new DrawLine(this.state, true);
|
|
}
|
|
if (id == "freeform-button") {
|
|
this.drawFunction = new DrawFreeform(this.state, "X");
|
|
}
|
|
if (id == "erase-button") {
|
|
this.drawFunction = new DrawErase(this.state);
|
|
}
|
|
if (id == "move-button") {
|
|
this.drawFunction = new DrawMove(this.state);
|
|
}
|
|
if (id == "text-button") {
|
|
this.drawFunction = new DrawText(this.state, this.view);
|
|
}
|
|
if (id == "select-button") {
|
|
this.drawFunction = new DrawSelect(this.state);
|
|
}
|
|
this.state.commitDraw();
|
|
this.view.canvas.focus();
|
|
}
|
|
|
|
/**
|
|
* Handles the buttons in the UI.
|
|
* @param {string} id The ID of the element clicked.
|
|
*/
|
|
handleFileButton(id) {
|
|
$(".dialog").removeClass("visible");
|
|
$("#" + id + "-dialog").toggleClass("visible");
|
|
|
|
if (id == "import-button") {
|
|
$("#import-area").val("");
|
|
$("#import-area").focus();
|
|
}
|
|
|
|
if (id == "export-button") {
|
|
$("#export-area").val(this.state.outputText());
|
|
$("#export-area").select();
|
|
}
|
|
if (id == "clear-button") {
|
|
this.state.clear();
|
|
}
|
|
if (id == "undo-button") {
|
|
this.state.undo();
|
|
}
|
|
if (id == "redo-button") {
|
|
this.state.redo();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles key presses.
|
|
* @param {jQuery.Event} event
|
|
*/
|
|
handleKeyPress(event) {
|
|
if (!event.ctrlKey && !event.metaKey && event.keyCode != 13) {
|
|
this.drawFunction.handleKey(String.fromCharCode(event.keyCode));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handles key down events.
|
|
* @param {jQuery.Event} event
|
|
*/
|
|
handleKeyDown(event) {
|
|
// Override some special characters so that they can be handled in one place.
|
|
var specialKeyCode = null;
|
|
|
|
if (event.ctrlKey || event.metaKey) {
|
|
if (event.keyCode == 67) {
|
|
specialKeyCode = c.KEY_COPY;
|
|
}
|
|
if (event.keyCode == 86) {
|
|
specialKeyCode = c.KEY_PASTE;
|
|
}
|
|
if (event.keyCode == 90) {
|
|
this.state.undo();
|
|
}
|
|
if (event.keyCode == 89) {
|
|
this.state.redo();
|
|
}
|
|
if (event.keyCode == 88) {
|
|
specialKeyCode = c.KEY_CUT;
|
|
}
|
|
}
|
|
|
|
if (event.keyCode == 8) {
|
|
specialKeyCode = c.KEY_BACKSPACE;
|
|
}
|
|
if (event.keyCode == 13) {
|
|
specialKeyCode = c.KEY_RETURN;
|
|
}
|
|
if (event.keyCode == 38) {
|
|
specialKeyCode = c.KEY_UP;
|
|
}
|
|
if (event.keyCode == 40) {
|
|
specialKeyCode = c.KEY_DOWN;
|
|
}
|
|
if (event.keyCode == 37) {
|
|
specialKeyCode = c.KEY_LEFT;
|
|
}
|
|
if (event.keyCode == 39) {
|
|
specialKeyCode = c.KEY_RIGHT;
|
|
}
|
|
|
|
if (specialKeyCode != null) {
|
|
//event.preventDefault();
|
|
//event.stopPropagation();
|
|
this.drawFunction.handleKey(specialKeyCode);
|
|
}
|
|
}
|
|
}
|