diff --git a/js/editor/keyboard.js b/js/editor/keyboard.js new file mode 100644 index 0000000..2acdf4f --- /dev/null +++ b/js/editor/keyboard.js @@ -0,0 +1,6 @@ +export const isMac = /mac|iPhone|iPad|iPod/i.test(window.navigator.platform); + +// On macOS it’s more natural to use the Command key for shortcuts. +export function isCtrlKey(event) { + return isMac ? event.metaKey : event.ctrlKey; +} diff --git a/js/editor/main.js b/js/editor/main.js index 5ece19b..039aefb 100644 --- a/js/editor/main.js +++ b/js/editor/main.js @@ -14,6 +14,7 @@ import * as util from '../util.js'; import * as dialogs from './dialogs.js'; import { TOOLS, TOOL_ORDER, TOOL_SHORTCUTS, PALETTE, SPECIAL_PALETTE_ENTRIES, SPECIAL_TILE_BEHAVIOR, TILE_DESCRIPTIONS, transform_direction_bitmask } from './editordefs.js'; import { SVGConnection, Selection } from './helpers.js'; +import { isCtrlKey, isMac } from './keyboard.js'; import * as mouseops from './mouseops.js'; import { TILES_WITH_PROPS } from './tile-overlays.js'; @@ -147,8 +148,23 @@ export class Editor extends PrimaryView { if (! this.active) return; - if (ev.ctrlKey) { - if (ev.key === 'a') { + if (isCtrlKey(ev)) { + // macOS is really weird: + // + // | Keys pressed | `ev.key` | + // |--------------|----------| + // | A | "a" | + // | shift+A | "A" | + // | cmd+A | "a" | + // | cmd+shift+A | "a" !!! | + // | ctrl+shift+A | "A" | + // + // For some reason, with cmd the key is always lowercase. + // So we have to check the letter key case insensitively, + // and then manually check if shift is pressed. + const key = ev.key.toLowerCase(); + + if (key === 'a' && !ev.shiftKey) { // Select all if (TOOLS[this.current_tool].affects_selection) { // If we're in the middle of using a selection tool, cancel it @@ -161,7 +177,7 @@ export class Editor extends PrimaryView { this.commit_undo(); } } - else if (ev.key === 'A') { + else if (ev.key === 'a' && ev.shiftKey) { // Deselect if (TOOLS[this.current_tool].affects_selection) { // If we're in the middle of using a selection tool, cancel it @@ -173,10 +189,10 @@ export class Editor extends PrimaryView { this.commit_undo(); } } - else if (ev.key === 'z') { + else if (key === 'z' && !ev.shiftKey) { this.undo(); } - else if (ev.key === 'Z' || ev.key === 'y') { + else if ((key === 'z' && ev.shiftKey) || (key === 'y' && !ev.shiftKey)) { this.redo(); } else { @@ -208,10 +224,18 @@ export class Editor extends PrimaryView { } } else if (ev.key === '-') { - this.zoom_by(-1); + if (this.mouse_coords) + this.zoom_by(-1, this.mouse_coords[0], this.mouse_coords[1]); + else + this.zoom_by(-1); } - else if (ev.key === '=') { - this.zoom_by(+1); + // `=` because US keyboards have `+` on the same key as `=`, and `+` is shift-`=` + // `+` because many other keyboards have `+` on its own key, un-shifted, while `=` is shifted + else if (ev.key === '=' || ev.key === '+') { + if (this.mouse_coords) + this.zoom_by(+1, this.mouse_coords[0], this.mouse_coords[1]); + else + this.zoom_by(+1); } else if (ev.key === '1') { this.set_canvas_zoom(1); @@ -309,6 +333,8 @@ export class Editor extends PrimaryView { // Mouse wheel to zoom this.set_canvas_zoom(1); this.viewport_el.addEventListener('wheel', ev => { + if (!isCtrlKey(ev)) + return; // The delta is platform and hardware dependent and ultimately kind of useless, so just // treat each event as a click and hope for the best if (ev.deltaY === 0) @@ -400,7 +426,7 @@ export class Editor extends PrimaryView { tooltip.append(this.svg_icon('svg-icon-mouse2')); } else if (key) { - tooltip.append(mk('kbd', key)); + tooltip.append(mk('kbd', key === 'ctrl' && isMac ? 'cmd' : key)); } } diff --git a/js/editor/mouseops.js b/js/editor/mouseops.js index bd23cbe..a351780 100644 --- a/js/editor/mouseops.js +++ b/js/editor/mouseops.js @@ -8,6 +8,7 @@ import { mk, mk_svg, walk_grid } from '../util.js'; import { SPECIAL_TILE_BEHAVIOR } from './editordefs.js'; import { SVGConnection } from './helpers.js'; +import { isCtrlKey } from './keyboard.js'; import { TILES_WITH_PROPS } from './tile-overlays.js'; // TODO some minor grievances @@ -202,7 +203,7 @@ export class MouseOperation { } _update_modifiers(ev) { - this.ctrl = ev.ctrlKey; + this.ctrl = isCtrlKey(ev); this.shift = ev.shiftKey; this.alt = ev.altKey; }