From bef5550a959a96a9e851456ea9b1d2d44e28fd59 Mon Sep 17 00:00:00 2001 From: "Eevee (Evelyn Woods)" Date: Tue, 16 Apr 2024 05:23:56 -0600 Subject: [PATCH] Make mouse operations always exist, not only while clicking This allows for multi-eyedrop (where right-clicking the same cell cycles through everything in that cell) to finally work. Also fixes #72, I think. --- js/editor/main.js | 88 +++++++++++++++++++------------------------ js/editor/mouseops.js | 32 ++++++++-------- 2 files changed, 54 insertions(+), 66 deletions(-) diff --git a/js/editor/main.js b/js/editor/main.js index 65ce985..c4edc0d 100644 --- a/js/editor/main.js +++ b/js/editor/main.js @@ -210,6 +210,7 @@ export class Editor extends PrimaryView { // Level canvas and mouse handling this.mouse_coords = null; + this.mouse_ops = [null, new mouseops.PanOperation(this, 1), null]; // left, middle, right this.mouse_op = null; this.viewport_el.addEventListener('mousedown', ev => { this.mouse_coords = [ev.clientX, ev.clientY]; @@ -223,36 +224,10 @@ export class Editor extends PrimaryView { button = 0; } - let op_type = null; - if (button === 0) { - // Left button: activate tool - op_type = TOOLS[this.current_tool].op1; + this.set_mouse_button(button); + if (this.mouse_op) { + this.mouse_op.do_press(ev); } - else if (button === 1) { - // Middle button: always pan - op_type = mouseops.PanOperation; - // TODO how should this impact the hover? - } - else if (button === 2) { - // Right button: activate tool's alt mode - op_type = TOOLS[this.current_tool].op2; - } - - if (! op_type) - return; - - if (this.mouse_op && this.mouse_op instanceof op_type && - this.mouse_op.physical_button === button) - { - // Don't replace with the same operation! - } - else { - if (this.mouse_op) { - this.mouse_op.do_destroy(); - } - this.mouse_op = new op_type(this, ev.clientX, ev.clientY, ev.button, button); - } - this.mouse_op.do_press(ev); ev.preventDefault(); ev.stopPropagation(); @@ -284,9 +259,9 @@ export class Editor extends PrimaryView { this.statusbar_cursor.textContent = `—`; } - if (! this.mouse_op) - return; - this.mouse_op.do_move(ev); + if (this.mouse_op) { + this.mouse_op.do_move(ev); + } }); this.actual_viewport_el.addEventListener('mouseleave', () => { if (this.mouse_op) { @@ -302,10 +277,7 @@ export class Editor extends PrimaryView { ev.preventDefault(); this.mouse_op.do_commit(); - // If this was an op for a different button, switch back to the primary - if (this.mouse_op.physical_button !== 0) { - this.init_default_mouse_op(); - } + this.set_mouse_button(0); }); // Disable context menu, which interferes with right-click tools this.viewport_el.addEventListener('contextmenu', ev => { @@ -1167,17 +1139,31 @@ export class Editor extends PrimaryView { } this.current_tool = tool; this.tool_button_els[this.current_tool].classList.add('-selected'); - this.init_default_mouse_op(); + + // Left button: activate tool + this._init_mouse_op(0, this.current_tool && TOOLS[this.current_tool].op1); + // Right button: activate tool's alt mode + this._init_mouse_op(2, this.current_tool && TOOLS[this.current_tool].op2); + + this.set_mouse_button(0); + } + _init_mouse_op(button, op_type) { + if (this.mouse_ops[button] && op_type && this.mouse_ops[button] instanceof op_type) + // Don't recreate the same type of mouse operation + return; + + if (this.mouse_ops[button]) { + this.mouse_ops[button].do_destroy(); + this.mouse_ops[button] = null; + } + + if (op_type) { + this.mouse_ops[button] = new op_type(this, button); + } } - init_default_mouse_op() { - if (this.mouse_op) { - this.mouse_op.do_destroy(); - this.mouse_op = null; - } - if (this.current_tool && TOOLS[this.current_tool].op1) { - this.mouse_op = new TOOLS[this.current_tool].op1(this, ...(this.mouse_coords ?? [0, 0]), 0); // FIXME? - } + set_mouse_button(button) { + this.mouse_op = this.mouse_ops[button]; } show_palette_tooltip(key) { @@ -1330,8 +1316,10 @@ export class Editor extends PrimaryView { ctx.clearRect(0, 0, this.fg_tile_el.width, this.fg_tile_el.height); this.renderer.draw_single_tile_type( this.fg_tile.type.name, this.fg_tile, this.fg_tile_el); - if (this.mouse_op) { - this.mouse_op.handle_tile_updated(); + for (let mouse_op of this.mouse_ops) { + if (mouse_op) { + mouse_op.handle_tile_updated(); + } } } @@ -1340,8 +1328,10 @@ export class Editor extends PrimaryView { ctx.clearRect(0, 0, this.bg_tile_el.width, this.bg_tile_el.height); this.renderer.draw_single_tile_type( this.bg_tile.type.name, this.bg_tile, this.bg_tile_el); - if (this.mouse_op) { - this.mouse_op.handle_tile_updated(true); + for (let mouse_op of this.mouse_ops) { + if (mouse_op) { + mouse_op.handle_tile_updated(true); + } } } diff --git a/js/editor/mouseops.js b/js/editor/mouseops.js index 255f4b2..28bdea1 100644 --- a/js/editor/mouseops.js +++ b/js/editor/mouseops.js @@ -10,28 +10,23 @@ import { TILES_WITH_PROPS } from './tile-overlays.js'; const MOUSE_BUTTON_MASKS = [1, 4, 2]; // MouseEvent.button/buttons are ordered differently export class MouseOperation { - constructor(editor, client_x, client_y, physical_button) { + constructor(editor, physical_button) { this.editor = editor; this.is_held = false; this.physical_button = physical_button; this.alt_mode = physical_button !== 0; this.ctrl = false; this.shift = false; - //this._update_modifiers(ev); // FIXME how do i get this initially?? - - let [frac_cell_x, frac_cell_y] = this.editor.renderer.real_cell_coords_from_event({clientX: client_x, clientY: client_y}); - let cell_x = Math.floor(frac_cell_x); - let cell_y = Math.floor(frac_cell_y); // Client coordinates of the previous mouse event - this.prev_client_x = client_x; - this.prev_client_y = client_y; + this.prev_client_x = null; + this.prev_client_y = null; // Cell coordinates - this.prev_cell_x = cell_x; - this.prev_cell_y = cell_y; + this.prev_cell_x = null; + this.prev_cell_y = null; // Fractional cell coordinates - this.prev_frac_cell_x = frac_cell_x; - this.prev_frac_cell_y = frac_cell_y; + this.prev_frac_cell_x = null; + this.prev_frac_cell_y = null; // Same as above, but for the most recent click (so drag ops know where they started) this.click_client_x = null; @@ -40,10 +35,6 @@ export class MouseOperation { this.click_cell_y = null; this.click_frac_cell_x = null; this.click_frac_cell_y = null; - - // Start out with a hover effect - // FIXME no good, subclass hasn't finished setting itself up yet - //this.do_move({clientX: client_x, clientY: client_y}); } cell(x, y) { @@ -207,6 +198,8 @@ export class PanOperation extends MouseOperation { } } +// FIXME handle moving the mouse while the button is down; should continuously eyedrop +// (seems like that /should/ work...) export class EyedropOperation extends MouseOperation { constructor(...args) { super(...args); @@ -296,10 +289,13 @@ export class PencilOperation extends MouseOperation { handle_press(x, y) { this.draw_in_cell(x, y); } - handle_drag(client_x, client_y, frac_cell_x, frac_cell_y, _cell_x, _cell_y) { + handle_drag(client_x, client_y, frac_cell_x, frac_cell_y, cell_x, cell_y) { for (let [x, y] of this.iter_touched_cells(frac_cell_x, frac_cell_y)) { this.draw_in_cell(x, y); } + + // Also update the preview tile position + this.handle_hover(client_x, client_y, frac_cell_x, frac_cell_y, cell_x, cell_y); } draw_in_cell(x, y) { @@ -346,6 +342,8 @@ export class PencilOperation extends MouseOperation { // toggled) // - right-click to pick, same logic as pencil (which needs improving) // - ctrl-click to erase +// - wait, no. ctrl to like, fill the terrain layer regardless of the current tile's layer? atm +// you can't flood with an item usefully, it just fills the whole level // - reset the preview after a fill? is that ever necessary? export class FillOperation extends MouseOperation { constructor(...args) {