mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-01 18:22:41 -08:00
initial commit
This commit is contained in:
332
vendor/yomitan/js/pages/settings/keyboard-mouse-input-field.js
vendored
Normal file
332
vendor/yomitan/js/pages/settings/keyboard-mouse-input-field.js
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Copyright (C) 2023-2025 Yomitan Authors
|
||||
* Copyright (C) 2020-2022 Yomichan Authors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import {EventDispatcher} from '../../core/event-dispatcher.js';
|
||||
import {EventListenerCollection} from '../../core/event-listener-collection.js';
|
||||
import {getActiveButtons, getActiveModifiers} from '../../dom/document-util.js';
|
||||
import {HotkeyUtil} from '../../input/hotkey-util.js';
|
||||
|
||||
/**
|
||||
* @augments EventDispatcher<import('keyboard-mouse-input-field').Events>
|
||||
*/
|
||||
export class KeyboardMouseInputField extends EventDispatcher {
|
||||
/**
|
||||
* @param {HTMLInputElement} inputNode
|
||||
* @param {?HTMLButtonElement} mouseButton
|
||||
* @param {?import('environment').OperatingSystem} os
|
||||
* @param {?(pointerType: string) => boolean} [isPointerTypeSupported]
|
||||
*/
|
||||
constructor(inputNode, mouseButton, os, isPointerTypeSupported = null) {
|
||||
super();
|
||||
/** @type {HTMLInputElement} */
|
||||
this._inputNode = inputNode;
|
||||
/** @type {?HTMLButtonElement} */
|
||||
this._mouseButton = mouseButton;
|
||||
/** @type {?(pointerType: string) => boolean} */
|
||||
this._isPointerTypeSupported = isPointerTypeSupported;
|
||||
/** @type {HotkeyUtil} */
|
||||
this._hotkeyUtil = new HotkeyUtil(os);
|
||||
/** @type {EventListenerCollection} */
|
||||
this._eventListeners = new EventListenerCollection();
|
||||
/** @type {?string} */
|
||||
this._key = null;
|
||||
/** @type {import('input').Modifier[]} */
|
||||
this._modifiers = [];
|
||||
/** @type {Set<number>} */
|
||||
this._penPointerIds = new Set();
|
||||
/** @type {boolean} */
|
||||
this._mouseModifiersSupported = false;
|
||||
/** @type {boolean} */
|
||||
this._keySupported = false;
|
||||
}
|
||||
|
||||
/** @type {import('input').Modifier[]} */
|
||||
get modifiers() {
|
||||
return this._modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {?string} key
|
||||
* @param {import('input').Modifier[]} modifiers
|
||||
* @param {boolean} [mouseModifiersSupported]
|
||||
* @param {boolean} [keySupported]
|
||||
*/
|
||||
prepare(key, modifiers, mouseModifiersSupported = false, keySupported = false) {
|
||||
this.cleanup();
|
||||
|
||||
this._mouseModifiersSupported = mouseModifiersSupported;
|
||||
this._keySupported = keySupported;
|
||||
this.setInput(key, modifiers);
|
||||
/** @type {import('event-listener-collection').AddEventListenerArgs[]} */
|
||||
const events = [
|
||||
[this._inputNode, 'keydown', this._onModifierKeyDown.bind(this), false],
|
||||
[this._inputNode, 'keyup', this._onModifierKeyUp.bind(this), false],
|
||||
];
|
||||
if (mouseModifiersSupported && this._mouseButton !== null) {
|
||||
events.push(
|
||||
[this._mouseButton, 'mousedown', this._onMouseButtonMouseDown.bind(this), false],
|
||||
[this._mouseButton, 'pointerdown', this._onMouseButtonPointerDown.bind(this), false],
|
||||
[this._mouseButton, 'pointerover', this._onMouseButtonPointerOver.bind(this), false],
|
||||
[this._mouseButton, 'pointerout', this._onMouseButtonPointerOut.bind(this), false],
|
||||
[this._mouseButton, 'pointercancel', this._onMouseButtonPointerCancel.bind(this), false],
|
||||
[this._mouseButton, 'mouseup', this._onMouseButtonMouseUp.bind(this), false],
|
||||
[this._mouseButton, 'contextmenu', this._onMouseButtonContextMenu.bind(this), false],
|
||||
);
|
||||
}
|
||||
for (const args of events) {
|
||||
this._eventListeners.addEventListener(...args);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {?string} key
|
||||
* @param {import('input').Modifier[]} modifiers
|
||||
*/
|
||||
setInput(key, modifiers) {
|
||||
this._key = key;
|
||||
this._modifiers = this._sortModifiers(modifiers);
|
||||
this._updateDisplayString();
|
||||
}
|
||||
|
||||
/** */
|
||||
cleanup() {
|
||||
this._eventListeners.removeAllEventListeners();
|
||||
this._modifiers = [];
|
||||
this._key = null;
|
||||
this._mouseModifiersSupported = false;
|
||||
this._keySupported = false;
|
||||
this._penPointerIds.clear();
|
||||
}
|
||||
|
||||
/** */
|
||||
clearInputs() {
|
||||
this._updateModifiers([], null);
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
/**
|
||||
* @param {import('input').Modifier[]} modifiers
|
||||
* @returns {import('input').Modifier[]}
|
||||
*/
|
||||
_sortModifiers(modifiers) {
|
||||
return this._hotkeyUtil.sortModifiers(modifiers);
|
||||
}
|
||||
|
||||
/** */
|
||||
_updateDisplayString() {
|
||||
const displayValue = this._hotkeyUtil.getInputDisplayValue(this._key, this._modifiers);
|
||||
this._inputNode.value = displayValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {KeyboardEvent} e
|
||||
* @returns {Set<import('input').ModifierKey>}
|
||||
*/
|
||||
_getModifierKeys(e) {
|
||||
const modifiers = new Set(getActiveModifiers(e));
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/metaKey
|
||||
// https://askubuntu.com/questions/567731/why-is-shift-alt-being-mapped-to-meta
|
||||
// It works with mouse events on some platforms, so try to determine if metaKey is pressed.
|
||||
// This is a hack and only works when both Shift and Alt are not pressed.
|
||||
if (
|
||||
!modifiers.has('meta') &&
|
||||
e.key === 'Meta' &&
|
||||
!(
|
||||
modifiers.size === 2 &&
|
||||
modifiers.has('shift') &&
|
||||
modifiers.has('alt')
|
||||
)
|
||||
) {
|
||||
modifiers.add('meta');
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string|undefined} keyName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isModifierKey(keyName) {
|
||||
switch (keyName) {
|
||||
case 'AltLeft':
|
||||
case 'AltRight':
|
||||
case 'ControlLeft':
|
||||
case 'ControlRight':
|
||||
case 'MetaLeft':
|
||||
case 'MetaRight':
|
||||
case 'ShiftLeft':
|
||||
case 'ShiftRight':
|
||||
case 'OSLeft':
|
||||
case 'OSRight':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {KeyboardEvent} e
|
||||
*/
|
||||
_onModifierKeyDown(e) {
|
||||
e.preventDefault();
|
||||
|
||||
/** @type {string|undefined} */
|
||||
let key = e.code;
|
||||
if (key === 'Unidentified' || key === '') { key = void 0; }
|
||||
if (this._keySupported) {
|
||||
this._updateModifiers([...this._getModifierKeys(e)], this._isModifierKey(key) ? void 0 : key);
|
||||
} else {
|
||||
switch (key) {
|
||||
case 'Escape':
|
||||
case 'Backspace':
|
||||
this.clearInputs();
|
||||
break;
|
||||
default:
|
||||
this._addModifiers(this._getModifierKeys(e));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {KeyboardEvent} e
|
||||
*/
|
||||
_onModifierKeyUp(e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MouseEvent} e
|
||||
*/
|
||||
_onMouseButtonMouseDown(e) {
|
||||
e.preventDefault();
|
||||
this._addModifiers(getActiveButtons(e));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} e
|
||||
*/
|
||||
_onMouseButtonPointerDown(e) {
|
||||
if (!e.isPrimary) { return; }
|
||||
|
||||
let {pointerType, pointerId} = e;
|
||||
// Workaround for Firefox bug not detecting certain 'touch' events as 'pen' events.
|
||||
if (this._penPointerIds.has(pointerId)) { pointerType = 'pen'; }
|
||||
|
||||
if (
|
||||
typeof this._isPointerTypeSupported !== 'function' ||
|
||||
!this._isPointerTypeSupported(pointerType)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
this._addModifiers(getActiveButtons(e));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} e
|
||||
*/
|
||||
_onMouseButtonPointerOver(e) {
|
||||
const {pointerType, pointerId} = e;
|
||||
if (pointerType === 'pen') {
|
||||
this._penPointerIds.add(pointerId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} e
|
||||
*/
|
||||
_onMouseButtonPointerOut(e) {
|
||||
const {pointerId} = e;
|
||||
this._penPointerIds.delete(pointerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PointerEvent} e
|
||||
*/
|
||||
_onMouseButtonPointerCancel(e) {
|
||||
this._onMouseButtonPointerOut(e);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MouseEvent} e
|
||||
*/
|
||||
_onMouseButtonMouseUp(e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {MouseEvent} e
|
||||
*/
|
||||
_onMouseButtonContextMenu(e) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Iterable<import('input').Modifier>} newModifiers
|
||||
* @param {?string} [newKey]
|
||||
*/
|
||||
_addModifiers(newModifiers, newKey) {
|
||||
const modifiers = new Set(this._modifiers);
|
||||
for (const modifier of newModifiers) {
|
||||
modifiers.add(modifier);
|
||||
}
|
||||
this._updateModifiers([...modifiers], newKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('input').Modifier[]} modifiers
|
||||
* @param {?string} [newKey]
|
||||
*/
|
||||
_updateModifiers(modifiers, newKey) {
|
||||
modifiers = this._sortModifiers(modifiers);
|
||||
|
||||
let changed = false;
|
||||
if (typeof newKey !== 'undefined' && this._key !== newKey) {
|
||||
this._key = newKey;
|
||||
changed = true;
|
||||
}
|
||||
if (!this._areArraysEqual(this._modifiers, modifiers)) {
|
||||
this._modifiers = modifiers;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
this._updateDisplayString();
|
||||
if (changed) {
|
||||
this.trigger('change', {modifiers: this._modifiers, key: this._key});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template [T=unknown]
|
||||
* @param {T[]} array1
|
||||
* @param {T[]} array2
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_areArraysEqual(array1, array2) {
|
||||
const length = array1.length;
|
||||
if (length !== array2.length) { return false; }
|
||||
|
||||
for (let i = 0; i < length; ++i) {
|
||||
if (array1[i] !== array2[i]) { return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user