asciiflow2/js-lib/controller.js

281 lines
7.1 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';
/**
* 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);
}
}
}