Added back tool shortctus

Made Events an IIFE instead of a class, fixed a bug in the selection tool.
This commit is contained in:
unsettledgames 2021-11-09 12:19:57 +01:00
parent 3c62a1f0fd
commit 2ca5aa75b4
15 changed files with 110 additions and 97 deletions

View File

@ -1,5 +1,5 @@
class Events { const Events = (() => {
customCallback = {}; let customCallback = {};
/** Used to programmatically create an input event /** Used to programmatically create an input event
* *
@ -8,7 +8,7 @@ class Events {
* @param {*} alt Is alt pressed? * @param {*} alt Is alt pressed?
* @param {*} shift Is shift pressed? * @param {*} shift Is shift pressed?
*/ */
static simulateInput(keyCode, ctrl, alt, shift) { function simulateInput(keyCode, ctrl, alt, shift) {
// I just copy pasted this from stack overflow lol please have mercy // I just copy pasted this from stack overflow lol please have mercy
let keyboardEvent = document.createEvent("KeyboardEvent"); let keyboardEvent = document.createEvent("KeyboardEvent");
let initMethod = typeof keyboardEvent.initKeyboardEvent !== 'undefined' ? "initKeyboardEvent" : "initKeyEvent"; let initMethod = typeof keyboardEvent.initKeyboardEvent !== 'undefined' ? "initKeyboardEvent" : "initKeyEvent";
@ -34,7 +34,7 @@ class Events {
* @param {*} eventName The name of the event * @param {*} eventName The name of the event
* @returns * @returns
*/ */
static simulateMouseEvent (element, eventName) function simulateMouseEvent (element, eventName)
{ {
function extend(destination, source) { function extend(destination, source) {
for (let property in source) for (let property in source)
@ -98,7 +98,7 @@ class Events {
* @param {*} functionCallback The function to callback when the event is shoot * @param {*} functionCallback The function to callback when the event is shoot
* @param {...any} args Arguments for the callback * @param {...any} args Arguments for the callback
*/ */
static on(event, elementId, functionCallback, ...args) { function on(event, elementId, functionCallback, ...args) {
//if element provided is string, get the actual element //if element provided is string, get the actual element
const element = Util.getElement(elementId); const element = Util.getElement(elementId);
@ -116,7 +116,7 @@ class Events {
* @param {*} functionCallback The function to callback when the event is shoot * @param {*} functionCallback The function to callback when the event is shoot
* @param {...any} args Arguments for the callback * @param {...any} args Arguments for the callback
*/ */
static onChildren(event, parentElement, functionCallback, ...args) { function onChildren(event, parentElement, functionCallback, ...args) {
parentElement = Util.getElement(parentElement); parentElement = Util.getElement(parentElement);
const children = parentElement.children; const children = parentElement.children;
@ -131,9 +131,9 @@ class Events {
* @param {*} event The event to register to * @param {*} event The event to register to
* @param {*} functionCallback The function to call * @param {*} functionCallback The function to call
*/ */
static onCustom(event, functionCallback) { function onCustom(event, functionCallback) {
if (customCallback[event] === undefined) if (customCallback[event] === undefined)
customCallback[event] = []; customCallback[event] = [];
customCallback[event].push(functionCallback); customCallback[event].push(functionCallback);
} }
@ -143,9 +143,18 @@ class Events {
* @param {*} event The event to emit * @param {*} event The event to emit
* @param {...any} args The arguments for that event * @param {...any} args The arguments for that event
*/ */
static emit(event, ...args) { function emit(event, ...args) {
if (customCallback[event] != undefined) if (customCallback[event] != undefined)
for (let i=0; i<customCallback[event].length; i++) for (let i=0; i<customCallback[event].length; i++)
customCallback[event][i](args); customCallback[event][i](args);
} }
}
return {
simulateInput,
simulateMouseEvent,
on,
onChildren,
onCustom,
emit
}
})();

View File

@ -50,10 +50,12 @@ const History = (() => {
} }
function undo () { function undo () {
console.log("undoing");
undoOrRedo('undo'); undoOrRedo('undo');
} }
function redo () { function redo () {
console.log("redoing");
undoOrRedo('redo'); undoOrRedo('redo');
} }

View File

@ -1,12 +1,11 @@
const Input = (() => { const Input = (() => {
let spaceKeyPressed = false;
let dragging = false; let dragging = false;
let currentMouseEvent = undefined; let currentMouseEvent = undefined;
// Hotkeys when pressing a key // Hotkeys when pressing a key
Events.on("keydown", document, KeyPress); Events.on("keydown", document, KeyPress);
// Update held keys when releasing a key // Update held keys when releasing a key
Events.on("keyup", window, function (e) {if (e.keyCode == 32) spaceKeyPressed = false;}); Events.on("keyup", window, function (e) {if (e.keyCode == 32) Events.emit("space-released");;});
// Update variables on mouse clicks // Update variables on mouse clicks
Events.on("mousedown", window, onMouseDown); Events.on("mousedown", window, onMouseDown);
@ -15,11 +14,19 @@ const Input = (() => {
function onMouseDown(event) { function onMouseDown(event) {
currentMouseEvent = event; currentMouseEvent = event;
dragging = true; dragging = true;
if (!Util.isChildOfByClass(event.target, "editor-top-menu")) {
TopMenuModule.closeMenu();
}
} }
function onMouseUp(event) { function onMouseUp(event) {
currentMouseEvent = event; currentMouseEvent = event;
dragging = false; dragging = false;
if (currentLayer != null && !Util.isChildOfByClass(event.target, "layers-menu-entry")) {
LayerList.closeOptionsMenu();
}
} }
function getCursorPosition(e) { function getCursorPosition(e) {
@ -47,20 +54,16 @@ const Input = (() => {
*/ */
function KeyPress(e) { function KeyPress(e) {
var keyboardEvent = window.event? event : e; var keyboardEvent = window.event? event : e;
console.log("pressed key");
//if the user is typing in an input field or renaming a layer, ignore these hotkeys, unless it's an enter key //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' || LayerList.isRenamingLayer) { if (document.activeElement.tagName == 'INPUT' || LayerList.isRenamingLayer()) {
if (e.keyCode == 13) { if (e.keyCode == 13) {
console.log("here");
LayerList.closeOptionsMenu(); LayerList.closeOptionsMenu();
} }
return; return;
} }
//if no document has been created yet, //if no document has been created yet or there is a dialog box open ignore hotkeys
//orthere is a dialog box open
//ignore hotkeys
if (!Startup.documentCreated() || Dialogue.isOpen()) return; if (!Startup.documentCreated() || Dialogue.isOpen()) return;
// //
@ -73,81 +76,68 @@ const Input = (() => {
switch (keyboardEvent.keyCode) { switch (keyboardEvent.keyCode) {
//pencil tool - 1, b //pencil tool - 1, b
case 49: case 66: case 49: case 66:
tool.pencil.switchTo(); Events.emit("tool-shortcut", "brush");
break; break;
// copy tool c // copy tool c
case 67: case 99: case 67: case 99:
if (keyboardEvent.ctrlKey && !dragging && currentTool.name == 'moveselection') { if (keyboardEvent.ctrlKey) {
copySelection(); Events.emit("ctrl+c");
} }
break; break;
//fill tool - 2, f //fill tool - 2, f
case 50: case 70: case 50: case 70:
tool.fill.switchTo(); Events.emit("tool-shortcut", "fill");
break; break;
//eyedropper - 3, e //eyedropper - 3, e
case 51: case 69: case 51: case 69:
tool.eyedropper.switchTo(); Events.emit("tool-shortcut", "eyedropper");
break; break;
//pan - 4, p, //pan - 4, p,
case 52: case 80: case 52: case 80:
tool.pan.switchTo(); Events.emit("tool-shortcut", "pan");
break; break;
// line - l
case 76: case 76:
tool.line.switchTo(); Events.emit("tool-shortcut", "line");
break; break;
//zoom - 5
case 53:
tool.zoom.switchTo();
break;
// eraser -6, r // eraser -6, r
case 54: case 82: case 54: case 82:
tool.eraser.switchTo() Events.emit("tool-shortcut", "eraser");
break; break;
// Rectangular selection // Rectangular selection m
case 77: case 109: case 77: case 109:
tool.rectselect.switchTo() Events.emit("tool-shortcut", "rectselect");
break; break;
// TODO: [ELLIPSE] Decide on a shortcut to use. "s" was chosen without any in-team consultation. // TODO: [ELLIPSE] Decide on a shortcut to use. "s" was chosen without any in-team consultation.
// ellipse tool, s // ellipse tool, s
case 83: case 83:
tool.ellipse.switchTo() //Events.emit("tool-shortcut", "ellipse");
break; break;
// rectangle tool, u // rectangle tool, u
case 85: case 85:
tool.rectangle.switchTo() Events.emit("tool-shortcut", "rectangle");
break; break;
// Paste tool // Paste tool
case 86: case 118: case 86: case 118:
if (keyboardEvent.ctrlKey && !dragging) { if (keyboardEvent.ctrlKey) {
pasteSelection(); Events.emit("ctrl+v");
} }
break; break;
case 88: case 120: case 88: case 120:
if (keyboardEvent.ctrlKey && !dragging && currentTool.name == 'moveselection') { if (keyboardEvent.ctrlKey) {
cutSelectionTool(); Events.emit("ctrl+x");
tool.pencil.switchTo();
} }
break; break;
//Z //Z
case 90: case 90: case 122:
//CTRL+ALT+Z redo //CTRL+ALT+Z redo
if (keyboardEvent.altKey && keyboardEvent.ctrlKey) { if (keyboardEvent.altKey && keyboardEvent.ctrlKey) {
History.redo(); History.redo();
if (!selectionCanceled) {
tool.pencil.switchTo()
}
} }
//CTRL+Z undo //CTRL+Z undo
else if (keyboardEvent.ctrlKey) { else if (keyboardEvent.ctrlKey) {
History.undo(); History.undo();
if (!selectionCanceled) {
tool.pencil.switchTo()
}
} }
//Z switch to zoom tool
else
tool.zoom.switchTo()
break; break;
//redo - ctrl y //redo - ctrl y
case 89: case 89:
@ -155,16 +145,12 @@ const Input = (() => {
History.redo(); History.redo();
break; break;
case 32: case 32:
spaceKeyPressed=true; Events.emit("space-pressed");
break; break;
} }
} }
} }
function spacePressed() {
return spaceKeyPressed;
}
function isDragging() { function isDragging() {
return dragging; return dragging;
} }
@ -174,7 +160,6 @@ const Input = (() => {
} }
return { return {
spacePressed,
isDragging, isDragging,
getCurrMouseEvent, getCurrMouseEvent,
getCursorPosition getCursorPosition

View File

@ -85,6 +85,7 @@ class Tool {
if (this.mainButton != undefined) if (this.mainButton != undefined)
this.mainButton.parentElement.classList.remove("selected"); this.mainButton.parentElement.classList.remove("selected");
this.isSelected = false; this.isSelected = false;
brushPreview.style.visibility = 'hidden';
} }
onStart(mousePos) { onStart(mousePos) {

View File

@ -1,20 +1,22 @@
const ToolManager = (() => { const ToolManager = (() => {
brushTool = new BrushTool("brush", {type: 'html'}, switchTool); tools = {};
eraserTool = new EraserTool("eraser", {type: 'html'}, switchTool);
rectangleTool = new RectangleTool("rectangle", {type: 'html'}, switchTool);
lineTool = new LineTool("line", {type: 'html'}, switchTool);
fillTool = new FillTool("fill", {type: 'cursor', style: 'crosshair'}, switchTool);
eyedropperTool = new EyedropperTool("eyedropper", {type: 'cursor', style: 'crosshair'}, switchTool);
panTool = new PanTool("pan", {type: 'custom'}, switchTool);
zoomTool = new ZoomTool("zoom", {type:'custom'});
moveSelectionTool = new MoveSelectionTool("moveselection", tools["brush"] = new BrushTool("brush", {type: 'html'}, switchTool);
{type:'cursor', style:'crosshair'}, switchTool, brushTool); tools["eraser"] = new EraserTool("eraser", {type: 'html'}, switchTool);
rectSelectTool = new RectangularSelectionTool("rectselect", tools["rectangle"] = new RectangleTool("rectangle", {type: 'html'}, switchTool);
{type: 'cursor', style:'crosshair'}, switchTool, moveSelectionTool); tools["line"] = new LineTool("line", {type: 'html'}, switchTool);
tools["fill"] = new FillTool("fill", {type: 'cursor', style: 'crosshair'}, switchTool);
currTool = brushTool; tools["eyedropper"] = new EyedropperTool("eyedropper", {type: 'cursor', style: 'crosshair'}, switchTool);
tools["pan"] = new PanTool("pan", {type: 'custom'}, switchTool);
tools["zoom"] = new ZoomTool("zoom", {type:'custom'});
tools["moveselection"] = new MoveSelectionTool("moveselection",
{type:'cursor', style:'crosshair'}, switchTool, tools["brush"]);
tools["rectselect"] = new RectangularSelectionTool("rectselect",
{type: 'cursor', style:'crosshair'}, switchTool, tools["moveselection"]);
currTool = tools["brush"];
currTool.onSelect(); currTool.onSelect();
canvasView.style.cursor = 'default'; canvasView.style.cursor = 'default';
@ -23,9 +25,15 @@ const ToolManager = (() => {
Events.on("mousedown", window, onMouseDown); Events.on("mousedown", window, onMouseDown);
Events.on("mousewheel", window, onMouseWheel); Events.on("mousewheel", window, onMouseWheel);
Events.onCustom("tool-shortcut", onShortcut);
function onShortcut(tool) {
switchTool(tools[tool]);
}
function onMouseWheel(mouseEvent) { function onMouseWheel(mouseEvent) {
let mousePos = Input.getCursorPosition(mouseEvent); let mousePos = Input.getCursorPosition(mouseEvent);
zoomTool.onMouseWheel(mousePos, mouseEvent.deltaY < 0 ? 'in' : 'out'); tools["zoom"].onMouseWheel(mousePos, mouseEvent.deltaY < 0 ? 'in' : 'out');
} }
function onMouseDown(mouseEvent) { function onMouseDown(mouseEvent) {
@ -40,7 +48,7 @@ const ToolManager = (() => {
currTool.onStart(mousePos, mouseEvent.target); currTool.onStart(mousePos, mouseEvent.target);
break; break;
case 2: case 2:
panTool.onStart(mousePos, mouseEvent.target); tools["pan"].onStart(mousePos, mouseEvent.target);
break; break;
case 3: case 3:
currTool.onRightStart(mousePos, mouseEvent.target); currTool.onRightStart(mousePos, mouseEvent.target);
@ -64,7 +72,7 @@ const ToolManager = (() => {
currTool.onDrag(mousePos, mouseEvent.target); currTool.onDrag(mousePos, mouseEvent.target);
break; break;
case 2: case 2:
panTool.onDrag(mousePos, mouseEvent.target); tools["pan"].onDrag(mousePos, mouseEvent.target);
break; break;
case 3: case 3:
currTool.onRightDrag(mousePos, mouseEvent.target); currTool.onRightDrag(mousePos, mouseEvent.target);
@ -87,7 +95,7 @@ const ToolManager = (() => {
currTool.onEnd(mousePos); currTool.onEnd(mousePos);
break; break;
case 2: case 2:
panTool.onEnd(mousePos); tools["pan"].onEnd(mousePos);
break; break;
case 3: case 3:
currTool.onRightEnd(mousePos, mouseEvent.target); currTool.onRightEnd(mousePos, mouseEvent.target);

View File

@ -14,6 +14,8 @@ const TopMenuModule = (() => {
//when you click a main menu items button //when you click a main menu items button
Events.on('click', menuButton, function (e) { Events.on('click', menuButton, function (e) {
// Close the already open menus
closeMenu();
// Select the item // Select the item
Util.select(e.target.parentElement); Util.select(e.target.parentElement);
}); });
@ -80,6 +82,8 @@ const TopMenuModule = (() => {
Events.on('click', currSubmenuButton, Dialogue.showDialogue, 'changelog'); Events.on('click', currSubmenuButton, Dialogue.showDialogue, 'changelog');
break; break;
} }
Events.on('click', currSubmenuButton, function() {TopMenuModule.closeMenu();});
} }
} }
} }

View File

@ -28,8 +28,6 @@
//=include tools/RectangularSelectionTool.js //=include tools/RectangularSelectionTool.js
//=include tools/MoveSelectionTool.js //=include tools/MoveSelectionTool.js
//=include ToolManager.js
/**init**/ /**init**/
//=include _consts.js //=include _consts.js
//=include Settings.js //=include Settings.js
@ -38,6 +36,7 @@
//=include Startup.js //=include Startup.js
//=include _pixelGrid.js //=include _pixelGrid.js
//=include EditorState.js //=include EditorState.js
//=include ToolManager.js
/**dropdown formatting**/ /**dropdown formatting**/
//=include PresetModule.js //=include PresetModule.js

View File

@ -9,6 +9,7 @@ class BrushTool extends ResizableTool {
onStart(mousePos) { onStart(mousePos) {
super.onStart(mousePos); super.onStart(mousePos);
new HistoryState().EditCanvas();
} }
onDrag(mousePos, cursorTarget) { onDrag(mousePos, cursorTarget) {

View File

@ -9,6 +9,7 @@ class EraserTool extends ResizableTool {
onStart(mousePos) { onStart(mousePos) {
super.onStart(mousePos); super.onStart(mousePos);
new HistoryState().EditCanvas();
} }
onDrag(mousePos, cursorTarget) { onDrag(mousePos, cursorTarget) {

View File

@ -12,6 +12,8 @@ class FillTool extends Tool {
return; return;
this.fill(mousePos); this.fill(mousePos);
currentLayer.updateLayerPreview(); currentLayer.updateLayerPreview();
new HistoryState().EditCanvas();
} }
@ -37,9 +39,6 @@ class FillTool extends Tool {
return (r == color[0] && g == color[1] && b == color[2] && a == color[3]); return (r == color[0] && g == color[1] && b == color[2] && a == color[3]);
} }
//save history state
new HistoryState().EditCanvas();
//temporary image holds the data while we change it //temporary image holds the data while we change it
let tempImage = currentLayer.context.getImageData(0, 0, canvasSize[0], canvasSize[1]); let tempImage = currentLayer.context.getImageData(0, 0, canvasSize[0], canvasSize[1]);

View File

@ -15,6 +15,8 @@ class LineTool extends ResizableTool {
this.startMousePos[0] = Math.floor(mousePos[0]) + 0.5; this.startMousePos[0] = Math.floor(mousePos[0]) + 0.5;
this.startMousePos[1] = Math.floor(mousePos[1]) + 0.5; this.startMousePos[1] = Math.floor(mousePos[1]) + 0.5;
new HistoryState().EditCanvas();
} }
onDrag(mousePos, cursorTarget) { onDrag(mousePos, cursorTarget) {

View File

@ -88,13 +88,13 @@ class MoveSelectionTool extends Tool {
// don't override the coloured pixels in the canvas // don't override the coloured pixels in the canvas
let underlyingImageData = currentLayer.context.getImageData( let underlyingImageData = currentLayer.context.getImageData(
this.currSelection.left, this.currSelection.top, this.currSelection.left, this.currSelection.top,
this.currSelection.width, this.currSelection.height this.currSelection.width+1, this.currSelection.height+1
); );
let pasteData = this.currSelection.data.data;
for (let i=0; i<underlyingImageData.data.length; i+=4) { for (let i=0; i<underlyingImageData.data.length; i+=4) {
let currentMovePixel = [ let currentMovePixel = [
this.currSelection.data.data[i], this.currSelection.data.data[i+1], pasteData[i], pasteData[i+1], pasteData[i+2], pasteData[i+3]
this.currSelection.data.data[i+2], this.currSelection.data.data[i+3]
]; ];
let currentUnderlyingPixel = [ let currentUnderlyingPixel = [
@ -104,26 +104,25 @@ class MoveSelectionTool extends Tool {
// If the pixel of the clipboard is empty, but the one below it isn't, I use the pixel below // If the pixel of the clipboard is empty, but the one below it isn't, I use the pixel below
if (Util.isPixelEmpty(currentMovePixel)) { if (Util.isPixelEmpty(currentMovePixel)) {
if (!Util.isPixelEmpty(underlyingImageData)) { if (!Util.isPixelEmpty(currentUnderlyingPixel)) {
this.currSelection.data.data[i] = currentUnderlyingPixel[0]; pasteData[i] = currentUnderlyingPixel[0];
this.currSelection.data.data[i+1] = currentUnderlyingPixel[1]; pasteData[i+1] = currentUnderlyingPixel[1];
this.currSelection.data.data[i+2] = currentUnderlyingPixel[2]; pasteData[i+2] = currentUnderlyingPixel[2];
this.currSelection.data.data[i+3] = currentUnderlyingPixel[3]; pasteData[i+3] = currentUnderlyingPixel[3];
} }
} }
} }
currentLayer.context.putImageData( console.log("Paste: " + pasteData.length + ", underlying: " + underlyingImageData.data.length);
this.currSelection.data,
this.currSelection.left, currentLayer.context.putImageData(new ImageData(pasteData, this.currSelection.width+1),
this.currSelection.top); this.currSelection.left, this.currSelection.top
);
this.currSelection = undefined; this.currSelection = undefined;
currentLayer.updateLayerPreview(); currentLayer.updateLayerPreview();
VFXLayer.canvas.style.zIndex = MIN_Z_INDEX; VFXLayer.canvas.style.zIndex = MIN_Z_INDEX;
// Saving history
new HistoryState().EditCanvas();
// Switch to brush // Switch to brush
this.switchFunc(this.endTool); this.switchFunc(this.endTool);
} }

View File

@ -1,3 +1,5 @@
// TODO: FIX SELECTION
class RectangleTool extends ResizableTool { class RectangleTool extends ResizableTool {
// Saving the empty rect svg // Saving the empty rect svg
emptyRectangleSVG = document.getElementById("rectangle-empty-button-svg"); emptyRectangleSVG = document.getElementById("rectangle-empty-button-svg");
@ -41,6 +43,8 @@ class RectangleTool extends ResizableTool {
this.startMousePos[0] = Math.floor(mousePos[0] / zoom) + 0.5; this.startMousePos[0] = Math.floor(mousePos[0] / zoom) + 0.5;
this.startMousePos[1] = Math.floor(mousePos[1] / zoom) + 0.5; this.startMousePos[1] = Math.floor(mousePos[1] / zoom) + 0.5;
new HistoryState().EditCanvas();
} }
onDrag(mousePos, cursorTarget) { onDrag(mousePos, cursorTarget) {

View File

@ -14,8 +14,6 @@ class RectangularSelectionTool extends SelectionTool {
onStart(mousePos) { onStart(mousePos) {
super.onStart(mousePos); super.onStart(mousePos);
// Saving the canvas
new HistoryState().EditCanvas();
// Putting the vfx layer on top of everything // Putting the vfx layer on top of everything
VFXLayer.canvas.style.zIndex = MAX_Z_INDEX; VFXLayer.canvas.style.zIndex = MAX_Z_INDEX;
@ -51,6 +49,7 @@ class RectangularSelectionTool extends SelectionTool {
onEnd(mousePos) { onEnd(mousePos) {
super.onEnd(mousePos); super.onEnd(mousePos);
new HistoryState().EditCanvas();
// Getting the end position // Getting the end position
this.endMousePos[0] = Math.round(this.endMousePos[0] / zoom) + 0.5; this.endMousePos[0] = Math.round(this.endMousePos[0] / zoom) + 0.5;

View File

@ -1,4 +1,4 @@
<ul id="main-menu"> <ul id="main-menu" class="editor-top-menu">
<li class="logo">Lospec Pixel Editor</li> <li class="logo">Lospec Pixel Editor</li>
<li> <li>
<button>File</button> <button>File</button>