From 41aa0c8efebf2e443ba7372762948b5e56cfd990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=9C=E7=89=A9=E3=83=AA=E3=83=B3?= Date: Thu, 9 Oct 2025 09:29:47 +0900 Subject: [PATCH] =?UTF-8?q?[=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0=E7=B5=B5?= =?UTF-8?q?=E6=96=87=E5=AD=97beta]Mac=E3=81=AECmd=E3=82=AD=E3=83=BC?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=E3=81=A8Ctrl/Cmd+Arrow=E3=82=AD=E3=83=BC?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=20(#16621)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [カスタム絵文字beta]MacのCmdキー対応とCtrl/Cmd+Arrowキー対応 * Update packages/frontend/src/components/grid/MkGrid.vue Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> --------- Co-authored-by: かっこかり <67428053+kakkokari-gtyih@users.noreply.github.com> --- .../frontend/src/components/grid/MkGrid.vue | 262 ++++++++---------- packages/frontend/src/utility/hotkey.ts | 4 +- packages/frontend/src/utility/key-event.ts | 153 ---------- 3 files changed, 124 insertions(+), 295 deletions(-) delete mode 100644 packages/frontend/src/utility/key-event.ts diff --git a/packages/frontend/src/components/grid/MkGrid.vue b/packages/frontend/src/components/grid/MkGrid.vue index a175485a7e..96d9e35773 100644 --- a/packages/frontend/src/components/grid/MkGrid.vue +++ b/packages/frontend/src/components/grid/MkGrid.vue @@ -71,7 +71,7 @@ import { import * as os from '@/os.js'; import { createColumn } from '@/components/grid/column.js'; import { createRow, defaultGridRowSetting, resetRow } from '@/components/grid/row.js'; -import { handleKeyEvent } from '@/utility/key-event.js'; +import { makeHotkey } from '@/utility/hotkey.js'; type RowHolder = { row: GridRow, @@ -289,161 +289,143 @@ function onKeyDown(ev: KeyboardEvent) { const max = availableBounds.value; const bounds = rangedBounds.value; - handleKeyEvent(ev, [ - { - code: 'Delete', handler: () => { - if (rangedRows.value.length > 0) { - if (rowSetting.events.delete) { - rowSetting.events.delete(rangedRows.value); - } - } else { - const context = createContext(); - removeDataFromGrid(context, (cell) => { - emitCellValue(cell, undefined); - }); + makeHotkey({ + 'delete': () => { + if (rangedRows.value.length > 0) { + if (rowSetting.events.delete) { + rowSetting.events.delete(rangedRows.value); } - }, - }, - { - code: 'KeyC', modifiers: ['Control'], handler: () => { + } else { const context = createContext(); - copyGridDataToClipboard(data.value, context); - }, - }, - { - code: 'KeyV', modifiers: ['Control'], handler: async () => { - const _cells = cells.value; - const context = createContext(); - await pasteToGridFromClipboard(context, (row, col, parsedValue) => { - emitCellValue(_cells[row.index].cells[col.index], parsedValue); + removeDataFromGrid(context, (cell) => { + emitCellValue(cell, undefined); }); - }, + } }, - { - code: 'ArrowRight', modifiers: ['Control', 'Shift'], handler: () => { - updateSelectionRange({ - leftTop: { col: selectedCellAddress.col, row: bounds.leftTop.row }, - rightBottom: { col: max.rightBottom.col, row: bounds.rightBottom.row }, - }); - }, + 'ctrl+c|meta+c': () => { + const context = createContext(); + copyGridDataToClipboard(data.value, context); }, - { - code: 'ArrowLeft', modifiers: ['Control', 'Shift'], handler: () => { - updateSelectionRange({ - leftTop: { col: max.leftTop.col, row: bounds.leftTop.row }, - rightBottom: { col: selectedCellAddress.col, row: bounds.rightBottom.row }, - }); - }, + 'ctrl+v|meta+v': async () => { + const _cells = cells.value; + const context = createContext(); + await pasteToGridFromClipboard(context, (row, col, parsedValue) => { + emitCellValue(_cells[row.index].cells[col.index], parsedValue); + }); }, - { - code: 'ArrowUp', modifiers: ['Control', 'Shift'], handler: () => { - updateSelectionRange({ - leftTop: { col: bounds.leftTop.col, row: max.leftTop.row }, - rightBottom: { col: bounds.rightBottom.col, row: selectedCellAddress.row }, - }); - }, + 'ctrl+shift+right|meta+shift+right': () => { + updateSelectionRange({ + leftTop: { col: selectedCellAddress.col, row: bounds.leftTop.row }, + rightBottom: { col: max.rightBottom.col, row: bounds.rightBottom.row }, + }); }, - { - code: 'ArrowDown', modifiers: ['Control', 'Shift'], handler: () => { - updateSelectionRange({ - leftTop: { col: bounds.leftTop.col, row: selectedCellAddress.row }, - rightBottom: { col: bounds.rightBottom.col, row: max.rightBottom.row }, - }); - }, + 'ctrl+shift+left|meta+shift+left': () => { + updateSelectionRange({ + leftTop: { col: max.leftTop.col, row: bounds.leftTop.row }, + rightBottom: { col: selectedCellAddress.col, row: bounds.rightBottom.row }, + }); }, - { - code: 'ArrowRight', modifiers: ['Shift'], handler: () => { - updateSelectionRange({ - leftTop: { - col: bounds.leftTop.col < selectedCellAddress.col - ? bounds.leftTop.col + 1 - : selectedCellAddress.col, - row: bounds.leftTop.row, - }, - rightBottom: { - col: (bounds.rightBottom.col > selectedCellAddress.col || bounds.leftTop.col === selectedCellAddress.col) - ? bounds.rightBottom.col + 1 - : selectedCellAddress.col, - row: bounds.rightBottom.row, - }, - }); - }, + 'ctrl+shift+up|meta+shift+up': () => { + updateSelectionRange({ + leftTop: { col: bounds.leftTop.col, row: max.leftTop.row }, + rightBottom: { col: bounds.rightBottom.col, row: selectedCellAddress.row }, + }); }, - { - code: 'ArrowLeft', modifiers: ['Shift'], handler: () => { - updateSelectionRange({ - leftTop: { - col: (bounds.leftTop.col < selectedCellAddress.col || bounds.rightBottom.col === selectedCellAddress.col) - ? bounds.leftTop.col - 1 - : selectedCellAddress.col, - row: bounds.leftTop.row, - }, - rightBottom: { - col: bounds.rightBottom.col > selectedCellAddress.col - ? bounds.rightBottom.col - 1 - : selectedCellAddress.col, - row: bounds.rightBottom.row, - }, - }); - }, + 'ctrl+shift+down|meta+shift+down': () => { + updateSelectionRange({ + leftTop: { col: bounds.leftTop.col, row: selectedCellAddress.row }, + rightBottom: { col: bounds.rightBottom.col, row: max.rightBottom.row }, + }); }, - { - code: 'ArrowUp', modifiers: ['Shift'], handler: () => { - updateSelectionRange({ - leftTop: { - col: bounds.leftTop.col, - row: (bounds.leftTop.row < selectedCellAddress.row || bounds.rightBottom.row === selectedCellAddress.row) - ? bounds.leftTop.row - 1 - : selectedCellAddress.row, - }, - rightBottom: { - col: bounds.rightBottom.col, - row: bounds.rightBottom.row > selectedCellAddress.row - ? bounds.rightBottom.row - 1 - : selectedCellAddress.row, - }, - }); - }, + 'ctrl+right|meta+right': () => { + selectionCell({ col: max.rightBottom.col, row: selectedCellAddress.row }); }, - { - code: 'ArrowDown', modifiers: ['Shift'], handler: () => { - updateSelectionRange({ - leftTop: { - col: bounds.leftTop.col, - row: bounds.leftTop.row < selectedCellAddress.row - ? bounds.leftTop.row + 1 - : selectedCellAddress.row, - }, - rightBottom: { - col: bounds.rightBottom.col, - row: (bounds.rightBottom.row > selectedCellAddress.row || bounds.leftTop.row === selectedCellAddress.row) - ? bounds.rightBottom.row + 1 - : selectedCellAddress.row, - }, - }); - }, + 'ctrl+left|meta+left': () => { + selectionCell({ col: max.leftTop.col, row: selectedCellAddress.row }); }, - { - code: 'ArrowDown', handler: () => { - selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row + 1 }); - }, + 'ctrl+up|meta+up': () => { + selectionCell({ col: selectedCellAddress.col, row: max.leftTop.row }); }, - { - code: 'ArrowUp', handler: () => { - selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row - 1 }); - }, + 'ctrl+down|meta+down': () => { + selectionCell({ col: selectedCellAddress.col, row: max.rightBottom.row }); }, - { - code: 'ArrowRight', handler: () => { - selectionCell({ col: selectedCellAddress.col + 1, row: selectedCellAddress.row }); - }, + 'shift+right': () => { + updateSelectionRange({ + leftTop: { + col: bounds.leftTop.col < selectedCellAddress.col + ? bounds.leftTop.col + 1 + : selectedCellAddress.col, + row: bounds.leftTop.row, + }, + rightBottom: { + col: (bounds.rightBottom.col > selectedCellAddress.col || bounds.leftTop.col === selectedCellAddress.col) + ? bounds.rightBottom.col + 1 + : selectedCellAddress.col, + row: bounds.rightBottom.row, + }, + }); }, - { - code: 'ArrowLeft', handler: () => { - selectionCell({ col: selectedCellAddress.col - 1, row: selectedCellAddress.row }); - }, + 'shift+left': () => { + updateSelectionRange({ + leftTop: { + col: (bounds.leftTop.col < selectedCellAddress.col || bounds.rightBottom.col === selectedCellAddress.col) + ? bounds.leftTop.col - 1 + : selectedCellAddress.col, + row: bounds.leftTop.row, + }, + rightBottom: { + col: bounds.rightBottom.col > selectedCellAddress.col + ? bounds.rightBottom.col - 1 + : selectedCellAddress.col, + row: bounds.rightBottom.row, + }, + }); }, - ]); + 'shift+up': () => { + updateSelectionRange({ + leftTop: { + col: bounds.leftTop.col, + row: (bounds.leftTop.row < selectedCellAddress.row || bounds.rightBottom.row === selectedCellAddress.row) + ? bounds.leftTop.row - 1 + : selectedCellAddress.row, + }, + rightBottom: { + col: bounds.rightBottom.col, + row: bounds.rightBottom.row > selectedCellAddress.row + ? bounds.rightBottom.row - 1 + : selectedCellAddress.row, + }, + }); + }, + 'shift+down': () => { + updateSelectionRange({ + leftTop: { + col: bounds.leftTop.col, + row: bounds.leftTop.row < selectedCellAddress.row + ? bounds.leftTop.row + 1 + : selectedCellAddress.row, + }, + rightBottom: { + col: bounds.rightBottom.col, + row: (bounds.rightBottom.row > selectedCellAddress.row || bounds.leftTop.row === selectedCellAddress.row) + ? bounds.rightBottom.row + 1 + : selectedCellAddress.row, + }, + }); + }, + 'down': () => { + selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row + 1 }); + }, + 'up': () => { + selectionCell({ col: selectedCellAddress.col, row: selectedCellAddress.row - 1 }); + }, + 'right': () => { + selectionCell({ col: selectedCellAddress.col + 1, row: selectedCellAddress.row }); + }, + 'left': () => { + selectionCell({ col: selectedCellAddress.col - 1, row: selectedCellAddress.row }); + }, + }, [])(ev); break; } diff --git a/packages/frontend/src/utility/hotkey.ts b/packages/frontend/src/utility/hotkey.ts index d728cdfcb0..9c1e66a22e 100644 --- a/packages/frontend/src/utility/hotkey.ts +++ b/packages/frontend/src/utility/hotkey.ts @@ -50,12 +50,12 @@ let latestHotkey: Pattern & { callback: CallbackFunction } | null = null; //#endregion //#region impl -export const makeHotkey = (keymap: Keymap) => { +export const makeHotkey = (keymap: Keymap, ignoreElements = IGNORE_ELEMENTS) => { const actions = parseKeymap(keymap); return (ev: KeyboardEvent) => { if ('pswp' in window && window.pswp != null) return; if (window.document.activeElement != null) { - if (IGNORE_ELEMENTS.includes(window.document.activeElement.tagName.toLowerCase())) return; + if (ignoreElements.includes(window.document.activeElement.tagName.toLowerCase())) return; if (getHTMLElementOrNull(window.document.activeElement)?.isContentEditable) return; } for (const action of actions) { diff --git a/packages/frontend/src/utility/key-event.ts b/packages/frontend/src/utility/key-event.ts deleted file mode 100644 index 020a6c2174..0000000000 --- a/packages/frontend/src/utility/key-event.ts +++ /dev/null @@ -1,153 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -/** - * {@link KeyboardEvent.code} の値を表す文字列。不足分は適宜追加する - * @see https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_code_values - */ -export type KeyCode = ( - | 'Backspace' - | 'Tab' - | 'Enter' - | 'Shift' - | 'Control' - | 'Alt' - | 'Pause' - | 'CapsLock' - | 'Escape' - | 'Space' - | 'PageUp' - | 'PageDown' - | 'End' - | 'Home' - | 'ArrowLeft' - | 'ArrowUp' - | 'ArrowRight' - | 'ArrowDown' - | 'Insert' - | 'Delete' - | 'Digit0' - | 'Digit1' - | 'Digit2' - | 'Digit3' - | 'Digit4' - | 'Digit5' - | 'Digit6' - | 'Digit7' - | 'Digit8' - | 'Digit9' - | 'KeyA' - | 'KeyB' - | 'KeyC' - | 'KeyD' - | 'KeyE' - | 'KeyF' - | 'KeyG' - | 'KeyH' - | 'KeyI' - | 'KeyJ' - | 'KeyK' - | 'KeyL' - | 'KeyM' - | 'KeyN' - | 'KeyO' - | 'KeyP' - | 'KeyQ' - | 'KeyR' - | 'KeyS' - | 'KeyT' - | 'KeyU' - | 'KeyV' - | 'KeyW' - | 'KeyX' - | 'KeyY' - | 'KeyZ' - | 'MetaLeft' - | 'MetaRight' - | 'ContextMenu' - | 'F1' - | 'F2' - | 'F3' - | 'F4' - | 'F5' - | 'F6' - | 'F7' - | 'F8' - | 'F9' - | 'F10' - | 'F11' - | 'F12' - | 'NumLock' - | 'ScrollLock' - | 'Semicolon' - | 'Equal' - | 'Comma' - | 'Minus' - | 'Period' - | 'Slash' - | 'Backquote' - | 'BracketLeft' - | 'Backslash' - | 'BracketRight' - | 'Quote' - | 'Meta' - | 'AltGraph' -); - -/** - * 修飾キーを表す文字列。不足分は適宜追加する。 - */ -export type KeyModifier = ( - | 'Shift' - | 'Control' - | 'Alt' - | 'Meta' -); - -/** - * 押下されたキー以外の状態を表す文字列。不足分は適宜追加する。 - */ -export type KeyState = ( - | 'composing' - | 'repeat' -); - -export type KeyEventHandler = { - modifiers?: KeyModifier[]; - states?: KeyState[]; - code: KeyCode | 'any'; - handler: (event: KeyboardEvent) => void; -}; - -export function handleKeyEvent(event: KeyboardEvent, handlers: KeyEventHandler[]) { - function checkModifier(ev: KeyboardEvent, modifiers? : KeyModifier[]) { - if (modifiers) { - return modifiers.every(modifier => ev.getModifierState(modifier)); - } - return true; - } - - function checkState(ev: KeyboardEvent, states?: KeyState[]) { - if (states) { - return states.every(state => ev.getModifierState(state)); - } - return true; - } - - let hit = false; - for (const handler of handlers.filter(it => it.code === event.code)) { - if (checkModifier(event, handler.modifiers) && checkState(event, handler.states)) { - handler.handler(event); - hit = true; - break; - } - } - - if (!hit) { - for (const handler of handlers.filter(it => it.code === 'any')) { - handler.handler(event); - } - } -}