From 351d9b2e8e9ec8877b156a0c166e9e211d0e7007 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Thu, 14 Jan 2021 17:54:09 -0500 Subject: [PATCH] Update KeyboardMouseInputField (#1232) * Assign missing type * Update KeyboardMouseInputField to use an array instead of a string * Use "modifiers" instead of "value" or "inputs" * Simplify * Add support for using keys * Use bool args instead of a string --- .../js/settings/keyboard-mouse-input-field.js | 159 +++++++++++------- ext/bg/js/settings/profile-conditions-ui.js | 25 ++- ext/bg/js/settings/scan-inputs-controller.js | 22 ++- 3 files changed, 131 insertions(+), 75 deletions(-) diff --git a/ext/bg/js/settings/keyboard-mouse-input-field.js b/ext/bg/js/settings/keyboard-mouse-input-field.js index 08425562..448841f6 100644 --- a/ext/bg/js/settings/keyboard-mouse-input-field.js +++ b/ext/bg/js/settings/keyboard-mouse-input-field.js @@ -27,7 +27,7 @@ class KeyboardMouseInputField extends EventDispatcher { this._isPointerTypeSupported = isPointerTypeSupported; this._keySeparator = ' + '; this._inputNameMap = new Map(DocumentUtil.getModifierKeys(os)); - this._keyPriorities = new Map([ + this._modifierPriorities = new Map([ ['meta', -4], ['ctrl', -3], ['alt', -2], @@ -35,25 +35,29 @@ class KeyboardMouseInputField extends EventDispatcher { ]); this._mouseInputNamePattern = /^mouse(\d+)$/; this._eventListeners = new EventListenerCollection(); - this._value = ''; - this._type = null; + this._key = null; + this._modifiers = []; this._penPointerIds = new Set(); + this._mouseModifiersSupported = false; + this._keySupported = false; } - get value() { - return this._value; + get modifiers() { + return this._modifiers; } - prepare(value, type) { + prepare(key, modifiers, mouseModifiersSupported=false, keySupported=false) { this.cleanup(); - this._value = value; - const modifiers = this._splitValue(value); - const {displayValue} = this._getInputStrings(modifiers); + this._key = key; + this._modifiers = this._sortModifiers(modifiers); + this._mouseModifiersSupported = mouseModifiersSupported; + this._keySupported = keySupported; + this._updateDisplayString(); const events = [ [this._inputNode, 'keydown', this._onModifierKeyDown.bind(this), false] ]; - if (type === 'modifierInputs' && this._mouseButton !== null) { + if (mouseModifiersSupported && this._mouseButton !== null) { events.push( [this._mouseButton, 'mousedown', this._onMouseButtonMouseDown.bind(this), false], [this._mouseButton, 'pointerdown', this._onMouseButtonPointerDown.bind(this), false], @@ -64,7 +68,6 @@ class KeyboardMouseInputField extends EventDispatcher { [this._mouseButton, 'contextmenu', this._onMouseButtonContextMenu.bind(this), false] ); } - this._inputNode.value = displayValue; for (const args of events) { this._eventListeners.addEventListener(...args); } @@ -72,35 +75,33 @@ class KeyboardMouseInputField extends EventDispatcher { cleanup() { this._eventListeners.removeAllEventListeners(); - this._value = ''; - this._type = null; + this._modifiers = []; + this._key = null; + this._mouseModifiersSupported = false; + this._keySupported = false; this._penPointerIds.clear(); } clearInputs() { - this._updateInputs([]); + this._updateModifiers([], null); } // Private - _splitValue(value) { - return value.split(/[,;\s]+/).map((v) => v.trim().toLowerCase()).filter((v) => v.length > 0); - } - - _sortInputs(inputs) { + _sortModifiers(modifiers) { const pattern = this._mouseInputNamePattern; - const keyPriorities = this._keyPriorities; - const inputInfos = inputs.map((value, index) => { - const match = pattern.exec(value); + const keyPriorities = this._modifierPriorities; + const modifierInfos = modifiers.map((modifier, index) => { + const match = pattern.exec(modifier); if (match !== null) { - return [value, 1, Number.parseInt(match[1], 10), index]; + return [modifier, 1, Number.parseInt(match[1], 10), index]; } else { - let priority = keyPriorities.get(value); + let priority = keyPriorities.get(modifier); if (typeof priority === 'undefined') { priority = 0; } - return [value, 0, priority, index]; + return [modifier, 0, priority, index]; } }); - inputInfos.sort((a, b) => { + modifierInfos.sort((a, b) => { let i = a[1] - b[1]; if (i !== 0) { return i; } @@ -113,36 +114,37 @@ class KeyboardMouseInputField extends EventDispatcher { i = a[3] - b[3]; return i; }); - return inputInfos.map(([value]) => value); + return modifierInfos.map(([modifier]) => modifier); } - _getInputStrings(inputs) { - let value = ''; + _updateDisplayString() { let displayValue = ''; let first = true; - for (const input of inputs) { - const {name} = this._getInputName(input); + for (const modifier of this._modifiers) { + const {name} = this._getModifierName(modifier); if (first) { first = false; } else { - value += ', '; displayValue += this._keySeparator; } - value += input; displayValue += name; } - return {value, displayValue}; + if (this._key !== null) { + if (!first) { displayValue += this._keySeparator; } + displayValue += this._key; + } + this._inputNode.value = displayValue; } - _getInputName(value) { + _getModifierName(modifier) { const pattern = this._mouseInputNamePattern; - const match = pattern.exec(value); + const match = pattern.exec(modifier); if (match !== null) { return {name: `Mouse ${match[1]}`, type: 'mouse'}; } - let name = this._inputNameMap.get(value); - if (typeof name === 'undefined') { name = value; } + let name = this._inputNameMap.get(modifier); + if (typeof name === 'undefined') { name = modifier; } return {name, type: 'key'}; } @@ -166,24 +168,40 @@ class KeyboardMouseInputField extends EventDispatcher { return modifiers; } + _isModifierKey(keyName) { + switch (keyName) { + case 'Alt': + case 'Control': + case 'Meta': + case 'Shift': + return true; + default: + return false; + } + } + _onModifierKeyDown(e) { e.preventDefault(); const key = DocumentUtil.getKeyFromEvent(e); - switch (key) { - case 'Escape': - case 'Backspace': - this.clearInputs(); - break; - default: - this._addInputs(this._getModifierKeys(e)); - break; + 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; + } } } _onMouseButtonMouseDown(e) { e.preventDefault(); - this._addInputs(DocumentUtil.getActiveButtons(e)); + this._addModifiers(DocumentUtil.getActiveButtons(e)); } _onMouseButtonPointerDown(e) { @@ -200,7 +218,7 @@ class KeyboardMouseInputField extends EventDispatcher { return; } e.preventDefault(); - this._addInputs(DocumentUtil.getActiveButtons(e)); + this._addModifiers(DocumentUtil.getActiveButtons(e)); } _onMouseButtonPointerOver(e) { @@ -227,22 +245,41 @@ class KeyboardMouseInputField extends EventDispatcher { e.preventDefault(); } - _addInputs(newInputs) { - const inputs = new Set(this._splitValue(this._value)); - for (const input of newInputs) { - inputs.add(input); + _addModifiers(newModifiers, newKey) { + const modifiers = new Set(this._modifiers); + for (const modifier of newModifiers) { + modifiers.add(modifier); } - this._updateInputs([...inputs]); + this._updateModifiers([...modifiers], newKey); } - _updateInputs(inputs) { - inputs = this._sortInputs(inputs); + _updateModifiers(modifiers, newKey) { + modifiers = this._sortModifiers(modifiers); - const node = this._inputNode; - const {value, displayValue} = this._getInputStrings(inputs); - node.value = displayValue; - if (this._value === value) { return; } - this._value = value; - this.trigger('change', {value, displayValue}); + 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}); + } + } + + _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; } } diff --git a/ext/bg/js/settings/profile-conditions-ui.js b/ext/bg/js/settings/profile-conditions-ui.js index 69319574..77c9db77 100644 --- a/ext/bg/js/settings/profile-conditions-ui.js +++ b/ext/bg/js/settings/profile-conditions-ui.js @@ -525,11 +525,12 @@ class ProfileConditionUI { } } - _onModifierInputChange({validate, normalize}, {value}) { - const okay = this._validateValue(value, validate); - this._value = value; + _onModifierInputChange({validate, normalize}, {modifiers}) { + modifiers = this._joinModifiers(modifiers); + const okay = this._validateValue(modifiers, validate); + this._value = modifiers; if (okay) { - const normalizedValue = this._normalizeValue(value, normalize); + const normalizedValue = this._normalizeValue(modifiers, normalize); this.settingsController.setGlobalSetting(this.getPath('value'), normalizedValue); } } @@ -589,7 +590,7 @@ class ProfileConditionUI { let inputType = 'text'; let inputValue = value; let inputStep = null; - let mouseButtonHidden = true; + let showMouseButton = false; const events = []; const inputData = {validate, normalize}; const node = this._valueInput; @@ -603,9 +604,9 @@ class ProfileConditionUI { case 'modifierKeys': case 'modifierInputs': inputValue = null; - mouseButtonHidden = (type !== 'modifierInputs'); + showMouseButton = (type === 'modifierInputs'); this._kbmInputField = this._parent.parent.createKeyboardMouseInputField(node, this._mouseButton); - this._kbmInputField.prepare(value, type); + this._kbmInputField.prepare(null, this._splitModifiers(value), showMouseButton, false); events.push(['on', this._kbmInputField, 'change', this._onModifierInputChange.bind(this, inputData), false]); break; default: // 'string' @@ -624,7 +625,7 @@ class ProfileConditionUI { } else { node.removeAttribute('step'); } - this._mouseButtonContainer.hidden = mouseButtonHidden; + this._mouseButtonContainer.hidden = !showMouseButton; for (const args of events) { this._inputEventListeners.addGeneric(...args); } @@ -645,4 +646,12 @@ class ProfileConditionUI { _removeSelf() { this._parent.removeCondition(this); } + + _splitModifiers(modifiersString) { + return modifiersString.split(/[,;\s]+/).map((v) => v.trim().toLowerCase()).filter((v) => v.length > 0); + } + + _joinModifiers(modifiersArray) { + return modifiersArray.join(', '); + } } diff --git a/ext/bg/js/settings/scan-inputs-controller.js b/ext/bg/js/settings/scan-inputs-controller.js index 462b9683..b8b3f710 100644 --- a/ext/bg/js/settings/scan-inputs-controller.js +++ b/ext/bg/js/settings/scan-inputs-controller.js @@ -199,8 +199,8 @@ class ScanInputField { const isPointerTypeSupported = this._isPointerTypeSupported.bind(this); this._includeInputField = new KeyboardMouseInputField(includeInputNode, includeMouseButton, this._os, isPointerTypeSupported); this._excludeInputField = new KeyboardMouseInputField(excludeInputNode, excludeMouseButton, this._os, isPointerTypeSupported); - this._includeInputField.prepare(include, 'modifierInputs'); - this._excludeInputField.prepare(exclude, 'modifierInputs'); + this._includeInputField.prepare(null, this._splitModifiers(include), true, false); + this._excludeInputField.prepare(null, this._splitModifiers(exclude), true, false); this._eventListeners.on(this._includeInputField, 'change', this._onIncludeValueChange.bind(this)); this._eventListeners.on(this._excludeInputField, 'change', this._onExcludeValueChange.bind(this)); @@ -230,12 +230,14 @@ class ScanInputField { // Private - _onIncludeValueChange({value}) { - this._parent.setProperty(this._index, 'include', value, true); + _onIncludeValueChange({modifiers}) { + modifiers = this._joinModifiers(modifiers); + this._parent.setProperty(this._index, 'include', modifiers, true); } - _onExcludeValueChange({value}) { - this._parent.setProperty(this._index, 'exclude', value, true); + _onExcludeValueChange({modifiers}) { + modifiers = this._joinModifiers(modifiers); + this._parent.setProperty(this._index, 'exclude', modifiers, true); } _onRemoveClick(e) { @@ -296,4 +298,12 @@ class ScanInputField { this._node.dataset.showAdvanced = `${showAdvanced}`; this._parent.setProperty(this._index, 'options.showAdvanced', showAdvanced, false); } + + _splitModifiers(modifiersString) { + return modifiersString.split(/[,;\s]+/).map((v) => v.trim().toLowerCase()).filter((v) => v.length > 0); + } + + _joinModifiers(modifiersArray) { + return modifiersArray.join(', '); + } }