Refactored Input.js to Events.js

Started working on Input.js, this time it's used to handle mouse clicks and keyboard shortcuts.
This commit is contained in:
unsettledgames 2021-07-20 22:52:51 +02:00
parent 4525519071
commit 1b1290c017
14 changed files with 242 additions and 214 deletions

View File

@ -18,6 +18,7 @@ const ColorModule = (() => {
animation:100,
filter: ".noshrink",
draggable: ".draggable-colour",
// REFACTOR: Don't touch dragging, simulate a mouseup event instead
onEnd: function() {dragging = false}
});

48
js/Events.js Normal file
View File

@ -0,0 +1,48 @@
class Events {
/** Used to programmatically create an input event
*
* @param {*} keyCode KeyCode of the key to press
* @param {*} ctrl Is ctrl pressed?
* @param {*} alt Is alt pressed?
* @param {*} shift Is shift pressed?
*/
static simulateInput(keyCode, ctrl, alt, shift) {
// I just copy pasted this from stack overflow lol please have mercy
let keyboardEvent = document.createEvent("KeyboardEvent");
let initMethod = typeof keyboardEvent.initKeyboardEvent !== 'undefined' ? "initKeyboardEvent" : "initKeyEvent";
keyboardEvent[initMethod](
"keydown", // event type: keydown, keyup, keypress
true, // bubbles
true, // cancelable
window, // view: should be window
ctrl, // ctrlKey
alt, // altKey
shift, // shiftKey
false, // metaKey
keyCode, // keyCode: unsigned long - the virtual key code, else 0
keyCode // charCode: unsigned long - the Unicode character associated with the depressed key, else 0
);
document.dispatchEvent(keyboardEvent);
}
static on(event, elementId, functionCallback, ...args) {
//if element provided is string, get the actual element
const element = Util.getElement(elementId);
element.addEventListener(event,
function (e) {
functionCallback(...args, e);
});
}
static onChildren(event, parentElement, functionCallback, ...args) {
parentElement = Util.getElement(parentElement);
const children = parentElement.children;
//loop through children and add onClick listener
for (var i = 0; i < children.length; i++) {
on(event, children[i], functionCallback, ...args);
}
}
}

View File

@ -2,7 +2,7 @@ const FileManager = (() => {
// Binding the browse holder change event to file loading
const browseHolder = document.getElementById('open-image-browse-holder');
Input.on('change', browseHolder, loadFile);
Events.on('change', browseHolder, loadFile);
function saveProject() {
//create name

View File

@ -29,8 +29,8 @@ const History = (() => {
let undoStates = [];
let redoStates = [];
Input.on('click', 'undo-button', undo);
Input.on('click', 'redo-button', redo);
Events.on('click', 'undo-button', undo);
Events.on('click', 'redo-button', redo);
//rename to add undo state
function saveHistoryState (state) {

View File

@ -1,48 +1,155 @@
class Input {
/** Used to programmatically create an input event
const Input = (() => {
let spaceKeyPressed = false;
let dragging = false;
// Hotkeys when pressing a key
Events.on("keydown", document, KeyPress);
// Update held keys when releasing a key
Events.on("keyup", window, function (e) {
if (e.keyCode == 32) spaceKeyPressed = false;
});
// Update variables on mouse clicks
Events.on("mousedown", window, onMouseDown);
Events.on("mouseup", window, onMouseUp);
function onMouseDown(event) {
dragging = true;
}
function onMouseUp(event) {
dragging = false;
}
/** Just listens to hotkeys and calls the linked functions
*
* @param {*} keyCode KeyCode of the key to press
* @param {*} ctrl Is ctrl pressed?
* @param {*} alt Is alt pressed?
* @param {*} shift Is shift pressed?
* @param {*} e
*/
static simulateInput(keyCode, ctrl, alt, shift) {
// I just copy pasted this from stack overflow lol please have mercy
let keyboardEvent = document.createEvent("KeyboardEvent");
let initMethod = typeof keyboardEvent.initKeyboardEvent !== 'undefined' ? "initKeyboardEvent" : "initKeyEvent";
function KeyPress(e) {
var keyboardEvent = window.event? event : e;
keyboardEvent[initMethod](
"keydown", // event type: keydown, keyup, keypress
true, // bubbles
true, // cancelable
window, // view: should be window
ctrl, // ctrlKey
alt, // altKey
shift, // shiftKey
false, // metaKey
keyCode, // keyCode: unsigned long - the virtual key code, else 0
keyCode // charCode: unsigned long - the Unicode character associated with the depressed key, else 0
);
document.dispatchEvent(keyboardEvent);
}
static on(event, elementId, functionCallback, ...args) {
//if element provided is string, get the actual element
const element = Util.getElement(elementId);
//if the user is typing in an input field or renaming a layer, ignore these hotkeys, unless it's an enter key
if (document.activeElement.tagName == 'INPUT' || isRenamingLayer) {
if (e.keyCode == 13) {
currentLayer.closeOptionsMenu();
}
return;
}
element.addEventListener(event,
function (e) {
functionCallback(...args, e);
});
}
//if no document has been created yet,
//orthere is a dialog box open
//ignore hotkeys
if (!documentCreated || Dialogue.isOpen()) return;
static onChildren(event, parentElement, functionCallback, ...args) {
parentElement = Util.getElement(parentElement);
const children = parentElement.children;
//loop through children and add onClick listener
for (var i = 0; i < children.length; i++) {
on(event, children[i], functionCallback, ...args);
//
if (e.key === "Escape") {
if (!selectionCanceled) {
tool.pencil.switchTo();
}
}
else {
switch (keyboardEvent.keyCode) {
//pencil tool - 1, b
case 49: case 66:
tool.pencil.switchTo();
break;
// copy tool c
case 67: case 99:
if (keyboardEvent.ctrlKey && !dragging && currentTool.name == 'moveselection') {
copySelection();
}
break;
//fill tool - 2, f
case 50: case 70:
tool.fill.switchTo();
break;
//eyedropper - 3, e
case 51: case 69:
tool.eyedropper.switchTo();
break;
//pan - 4, p,
case 52: case 80:
tool.pan.switchTo();
break;
case 76:
tool.line.switchTo();
break;
//zoom - 5
case 53:
tool.zoom.switchTo();
break;
// eraser -6, r
case 54: case 82:
tool.eraser.switchTo()
break;
// Rectangular selection
case 77: case 109:
tool.rectselect.switchTo()
break;
// TODO: [ELLIPSE] Decide on a shortcut to use. "s" was chosen without any in-team consultation.
// ellipse tool, s
case 83:
tool.ellipse.switchTo()
break;
// rectangle tool, u
case 85:
tool.rectangle.switchTo()
break;
// Paste tool
case 86: case 118:
if (keyboardEvent.ctrlKey && !dragging) {
pasteSelection();
}
break;
case 88: case 120:
if (keyboardEvent.ctrlKey && !dragging && currentTool.name == 'moveselection') {
cutSelectionTool();
tool.pencil.switchTo();
}
break;
//Z
case 90:
//CTRL+ALT+Z redo
if (keyboardEvent.altKey && keyboardEvent.ctrlKey) {
History.redo();
if (!selectionCanceled) {
tool.pencil.switchTo()
}
}
//CTRL+Z undo
else if (keyboardEvent.ctrlKey) {
History.undo();
if (!selectionCanceled) {
tool.pencil.switchTo()
}
}
//Z switch to zoom tool
else
tool.zoom.switchTo()
break;
//redo - ctrl y
case 89:
if (keyboardEvent.ctrlKey)
History.redo();
break;
case 32:
spaceKeyPressed=true;
break;
}
}
}
}
function spacePressed() {
return spaceKeyPressed;
}
function isDragging() {
return dragging;
}
return {
spacePressed,
isDragging
}
})();

View File

@ -3,8 +3,8 @@ const Startup = (() => {
let firstPixel = true;
let splashPostfix = '';
Input.on('click', 'create-button', create, false);
Input.on('click', 'create-button-splash', create, true);
Events.on('click', 'create-button', create, false);
Events.on('click', 'create-button-splash', create, true);
function create(isSplash) {
// If I'm creating from the splash menu, I append '-splash' so I get the corresponding values

View File

@ -13,7 +13,7 @@ const TopMenuModule = (() => {
const menuButton = menuItem.children[0];
//when you click a main menu items button
Input.on('click', menuButton, function (e) {
Events.on('click', menuButton, function (e) {
// Select the item
Util.select(e.target.parentElement);
});
@ -29,16 +29,16 @@ const TopMenuModule = (() => {
switch (currSubmenuButton.textContent) {
case 'New':
Input.on('click', currSubmenuButton, Dialogue.showDialogue, 'new-pixel');
Events.on('click', currSubmenuButton, Dialogue.showDialogue, 'new-pixel');
break;
case 'Save project':
Input.on('click', currSubmenuButton, FileManager.saveProject);
Events.on('click', currSubmenuButton, FileManager.saveProject);
break;
case 'Open':
Input.on('click', currSubmenuButton, FileManager.open);
Events.on('click', currSubmenuButton, FileManager.open);
break;
case 'Export':
Input.on('click', currSubmenuButton, FileManager.exportProject);
Events.on('click', currSubmenuButton, FileManager.exportProject);
break;
case 'Exit':
//if a document exists, make sure they want to delete it
@ -53,31 +53,31 @@ const TopMenuModule = (() => {
break;
// REFACTOR: move the binding to the Selection IIFE or something like that once it's done
case 'Paste':
Input.on('click', currSubmenuButton, pasteSelection);
Events.on('click', currSubmenuButton, pasteSelection);
break;
case 'Copy':
Input.on('click', currSubmenuButton, copySelection);
Events.on('click', currSubmenuButton, copySelection);
break;
case 'Cut':
Input.on('click', currSubmenuButton, cutSelectionTool);
Events.on('click', currSubmenuButton, cutSelectionTool);
break;
case 'Cancel':
Input.on('click', currSubmenuButton, tool.pencil.switchTo);
Events.on('click', currSubmenuButton, tool.pencil.switchTo);
break;
//Help Menu
case 'Settings':
//fill form with current settings values
//Util.setValue('setting-numberOfHistoryStates', settings.numberOfHistoryStates);
Input.on('click', currSubmenuButton, Dialogue.showDialogue, 'settings');
Events.on('click', currSubmenuButton, Dialogue.showDialogue, 'settings');
break;
case 'Help':
Input.on('click', currSubmenuButton, Dialogue.showDialogue, 'help');
Events.on('click', currSubmenuButton, Dialogue.showDialogue, 'help');
break;
case 'About':
Input.on('click', currSubmenuButton, Dialogue.showDialogue, 'about');
Events.on('click', currSubmenuButton, Dialogue.showDialogue, 'about');
break;
case 'Changelog':
Input.on('click', currSubmenuButton, Dialogue.showDialogue, 'changelog');
Events.on('click', currSubmenuButton, Dialogue.showDialogue, 'changelog');
break;
}
}

View File

@ -12,7 +12,7 @@ let modes = {
}
}
Input.on('click', 'switch-editor-mode-splash', function (e) {
Events.on('click', 'switch-editor-mode-splash', function (e) {
toggleMode();
});
@ -75,6 +75,6 @@ function toggleMode() {
switchMode('Advanced');
}
Input.on('click', 'switch-mode-button', function (e) {
Events.on('click', 'switch-mode-button', function (e) {
toggleMode();
});

View File

@ -1,128 +0,0 @@
// REFACTOR: rename Input into Events and have an Input IIFE to take care of hotkeys, cursor ecc?
var spacePressed = false;
/** Just listens to hotkeys and calls the linked functions
*
* @param {*} e
*/
function KeyPress(e) {
var keyboardEvent = window.event? event : e;
//if the user is typing in an input field or renaming a layer, ignore these hotkeys, unless it's an enter key
if (document.activeElement.tagName == 'INPUT' || isRenamingLayer) {
if (e.keyCode == 13) {
currentLayer.closeOptionsMenu();
}
return;
}
//if no document has been created yet,
//orthere is a dialog box open
//ignore hotkeys
if (!documentCreated || Dialogue.isOpen()) return;
//
if (e.key === "Escape") {
if (!selectionCanceled) {
tool.pencil.switchTo();
}
}
else {
switch (keyboardEvent.keyCode) {
//pencil tool - 1, b
case 49: case 66:
tool.pencil.switchTo();
break;
// copy tool c
case 67: case 99:
if (keyboardEvent.ctrlKey && !dragging && currentTool.name == 'moveselection') {
copySelection();
}
break;
//fill tool - 2, f
case 50: case 70:
tool.fill.switchTo();
break;
//eyedropper - 3, e
case 51: case 69:
tool.eyedropper.switchTo();
break;
//pan - 4, p,
case 52: case 80:
tool.pan.switchTo();
break;
case 76:
tool.line.switchTo();
break;
//zoom - 5
case 53:
tool.zoom.switchTo();
break;
// eraser -6, r
case 54: case 82:
tool.eraser.switchTo()
break;
// Rectangular selection
case 77: case 109:
tool.rectselect.switchTo()
break;
// TODO: [ELLIPSE] Decide on a shortcut to use. "s" was chosen without any in-team consultation.
// ellipse tool, s
case 83:
tool.ellipse.switchTo()
break;
// rectangle tool, u
case 85:
tool.rectangle.switchTo()
break;
// Paste tool
case 86: case 118:
if (keyboardEvent.ctrlKey && !dragging) {
pasteSelection();
}
break;
case 88: case 120:
if (keyboardEvent.ctrlKey && !dragging && currentTool.name == 'moveselection') {
cutSelectionTool();
tool.pencil.switchTo();
}
break;
//Z
case 90:
//CTRL+ALT+Z redo
if (keyboardEvent.altKey && keyboardEvent.ctrlKey)
History.redo();
if (!selectionCanceled) {
tool.pencil.switchTo()
}
//CTRL+Z undo
else if (keyboardEvent.ctrlKey) {
History.undo();
if (!selectionCanceled) {
tool.pencil.switchTo()
}
}
//Z switch to zoom tool
else
tool.zoom.switchTo()
break;
//redo - ctrl y
case 89:
if (keyboardEvent.ctrlKey)
History.redo();
break;
case 32:
spacePressed=true;
break;
}
}
}
document.onkeydown = KeyPress;
window.addEventListener("keyup", function (e) {
if (e.keyCode == 32) spacePressed = false;
});

View File

@ -24,7 +24,7 @@ let oldLayerName = null;
let dragStartLayer;
// Binding the add layer button to the function
Input.on('click',"add-layer-button", addLayer, false);
Events.on('click',"add-layer-button", addLayer, false);
/** Handler class for a single canvas (a single layer)
*
@ -475,7 +475,7 @@ function renameLayer(event) {
p.classList.add("layer-name-editable");
p.focus();
Input.simulateInput(65, true, false, false);
Events.simulateInput(65, true, false, false);
isRenamingLayer = true;
}

View File

@ -17,7 +17,7 @@ window.addEventListener("mousedown", function (mouseEvent) {
dragging = true;
//left or right click ?
if (mouseEvent.which == 1) {
if (spacePressed)
if (Input.spacePressed())
currentTool = tool.pan;
else if (mouseEvent.altKey)
currentTool = tool.eyedropper;

View File

@ -26,7 +26,7 @@ else{
console.log(settings);
//on clicking the save button in the settings dialog
Input.on('click', 'save-settings', saveSettings);
Events.on('click', 'save-settings', saveSettings);
function saveSettings() {
//check if values are valid

View File

@ -1,38 +1,38 @@
//pencil
Input.on('click',"pencil-button", function(){
Events.on('click',"pencil-button", function(){
tool.pencil.switchTo();
}, false);
//pencil bigger
Input.on('click',"pencil-bigger-button", function(){
Events.on('click',"pencil-bigger-button", function(){
tool.pencil.brushSize++;
}, false);
//pencil smaller
Input.on('click',"pencil-smaller-button", function(){
Events.on('click',"pencil-smaller-button", function(){
if(tool.pencil.brushSize > 1)
tool.pencil.brushSize--;
}, false);
//eraser
Input.on('click',"eraser-button", function(){
Events.on('click',"eraser-button", function(){
console.log("selecting eraser");
tool.eraser.switchTo();
}, false);
//eraser bigger
Input.on('click',"eraser-bigger-button", function(){
Events.on('click',"eraser-bigger-button", function(){
tool.eraser.brushSize++;
}, false);
//eraser smaller
Input.on('click',"eraser-smaller-button", function(e){
Events.on('click',"eraser-smaller-button", function(e){
if(tool.eraser.brushSize > 1)
tool.eraser.brushSize--;
}, false);
// rectangle
Input.on('click','rectangle-button', function(e){
Events.on('click','rectangle-button', function(e){
// If the user clicks twice on the button, they change the draw mode
if (currentTool.name == 'rectangle') {
if (rectangleDrawMode == 'empty') {
@ -50,7 +50,7 @@ Input.on('click','rectangle-button', function(e){
}, false);
// ellipse
Input.on('click','ellipse-button', function(e){
Events.on('click','ellipse-button', function(e){
// If the user clicks twice on the button, they change the draw mode
if (currentTool.name == 'ellipse') {
if (ellipseDrawMode == 'empty') {
@ -68,57 +68,57 @@ Input.on('click','ellipse-button', function(e){
}, false);
// rectangle bigger
Input.on('click',"rectangle-bigger-button", function(){
Events.on('click',"rectangle-bigger-button", function(){
tool.rectangle.brushSize++;
}, false);
// rectangle smaller
Input.on('click',"rectangle-smaller-button", function(e){
Events.on('click',"rectangle-smaller-button", function(e){
if(tool.rectangle.brushSize > 1)
tool.rectangle.brushSize--;
}, false);
// ellipse bigger
Input.on('click',"ellipse-bigger-button", function(){
Events.on('click',"ellipse-bigger-button", function(){
tool.ellipse.brushSize++;
}, false);
// ellipse smaller
Input.on('click',"ellipse-smaller-button", function(e){
Events.on('click',"ellipse-smaller-button", function(e){
if(tool.ellipse.brushSize > 1)
tool.ellipse.brushSize--;
}, false);
//fill
Input.on('click',"fill-button", function(){
Events.on('click',"fill-button", function(){
tool.fill.switchTo();
}, false);
//pan
Input.on('click',"pan-button", function(){
Events.on('click',"pan-button", function(){
tool.pan.switchTo();
}, false);
//eyedropper
Input.on('click',"eyedropper-button", function(){
Events.on('click',"eyedropper-button", function(){
tool.eyedropper.switchTo();
}, false);
//rectangular selection button
Input.on('click', "rectselect-button", function(){
Events.on('click', "rectselect-button", function(){
tool.rectselect.switchTo();
}, false);
//line
Input.on('click',"line-button", function(){
Events.on('click',"line-button", function(){
tool.line.switchTo();
}, false);
Input.on('click',"line-bigger-button", function(){
Events.on('click',"line-bigger-button", function(){
tool.line.brushSize++;
}, false);
Input.on('click',"line-smaller-button", function(){
Events.on('click',"line-smaller-button", function(){
if(tool.line.brushSize > 1)
tool.line.brushSize--;
}, false);

View File

@ -4,7 +4,7 @@
//=include _pixelEditorUtility.js
//=include lib/sortable.js
//=include Util.js
//=include Input.js
//=include Events.js
//=include Color.js
//=include Dialogue.js
//=include History.js
@ -42,7 +42,7 @@
//=include _loadPalette.js
/**event listeners**/
//=include _hotkeyListener.js
//=include Input.js
//=include _mouseEvents.js
/**buttons**/