From 0d00f7e1cf8a0fa1e2b1aa2732bceaae39f4e23c Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Wed, 9 Sep 2020 16:59:03 -0400 Subject: Scanning input generalization (#789) * Add inputs to options.scanning * Update CSS for mouse buttons * Update list counters * Set up HTML/CSS * Add input controller * Use new inputs * Include mouse buttons * Update how button inputs are detected * Add index/empty fields to the input details object * Update none check for scanning modifier * Remove old settings * Remove unused global --- ext/mixed/js/display.js | 3 +- ext/mixed/js/document-util.js | 25 ++++++++++++ ext/mixed/js/text-scanner.js | 88 ++++++++++++++++++++++++++----------------- 3 files changed, 79 insertions(+), 37 deletions(-) (limited to 'ext/mixed/js') diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 70b3895a..ea6b52c0 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -239,10 +239,9 @@ class Display extends EventDispatcher { selectedParser: options.parsing.selectedParser, termSpacing: options.parsing.termSpacing, scanning: { + inputs: scanning.inputs, deepContentScan: scanning.deepDomScan, selectText: scanning.selectText, - modifier: scanning.modifier, - useMiddleMouse: scanning.middleMouse, delay: scanning.delay, touchInputEnabled: scanning.touchInputEnabled, scanLength: scanning.length, diff --git a/ext/mixed/js/document-util.js b/ext/mixed/js/document-util.js index 0b72ff9a..58b0c759 100644 --- a/ext/mixed/js/document-util.js +++ b/ext/mixed/js/document-util.js @@ -188,6 +188,18 @@ class DocumentUtil { return modifiers; } + static getActiveModifiersAndButtons(event) { + const modifiers = this.getActiveModifiers(event); + this._getActiveButtons(event, modifiers); + return modifiers; + } + + static getActiveButtons(event) { + const buttons = new Set(); + this._getActiveButtons(event, buttons); + return buttons; + } + static getKeyFromEvent(event) { const key = event.key; return (typeof key === 'string' ? (key.length === 1 ? key.toUpperCase() : key) : ''); @@ -299,6 +311,19 @@ class DocumentUtil { return !(browser === 'firefox' || browser === 'firefox-mobile') || os === 'mac'; } + static _getActiveButtons(event, set) { + const {buttons} = event; + if (typeof buttons === 'number') { + for (let i = 0; i < 6; ++i) { + const buttonFlag = (1 << i); + if ((buttons & buttonFlag) !== 0) { + set.add(`mouse${i}`); + } + } + } + return set; + } + // Private _setImposterStyle(style, propertyName, value) { diff --git a/ext/mixed/js/text-scanner.js b/ext/mixed/js/text-scanner.js index f3e99577..6eafc82c 100644 --- a/ext/mixed/js/text-scanner.js +++ b/ext/mixed/js/text-scanner.js @@ -43,13 +43,12 @@ class TextScanner extends EventDispatcher { this._deepContentScan = false; this._selectText = false; - this._modifier = 'none'; - this._useMiddleMouse = false; this._delay = 0; this._touchInputEnabled = false; this._scanLength = 1; this._sentenceExtent = 1; this._layoutAwareScan = false; + this._inputs = []; this._enabled = false; this._eventListeners = new EventListenerCollection(); @@ -94,19 +93,19 @@ class TextScanner extends EventDispatcher { } } - setOptions({deepContentScan, selectText, modifier, useMiddleMouse, delay, touchInputEnabled, scanLength, sentenceExtent, layoutAwareScan}) { + setOptions({inputs, deepContentScan, selectText, delay, touchInputEnabled, scanLength, sentenceExtent, layoutAwareScan}) { + if (Array.isArray(inputs)) { + this._inputs = inputs.map(({include, exclude}) => ({ + include: this._getInputArray(include), + exclude: this._getInputArray(exclude) + })); + } if (typeof deepContentScan === 'boolean') { this._deepContentScan = deepContentScan; } if (typeof selectText === 'boolean') { this._selectText = selectText; } - if (typeof modifier === 'string') { - this._modifier = modifier; - } - if (typeof useMiddleMouse === 'boolean') { - this._useMiddleMouse = useMiddleMouse; - } if (typeof delay === 'number') { this._delay = delay; } @@ -184,7 +183,7 @@ class TextScanner extends EventDispatcher { } async search(textSource) { - return await this._search(textSource, {cause: 'script'}); + return await this._search(textSource, {cause: 'script', index: -1, empty: false}); } // Private @@ -242,17 +241,14 @@ class TextScanner extends EventDispatcher { return; } - const modifiers = DocumentUtil.getActiveModifiers(e); + const modifiers = DocumentUtil.getActiveModifiersAndButtons(e); this.trigger('activeModifiersChanged', {modifiers}); - if (!( - this._isScanningModifierPressed(this._modifier, e) || - (this._useMiddleMouse && DocumentUtil.isMouseButtonDown(e, 'auxiliary')) - )) { - return; - } + const inputInfo = this._getMatchingInputGroup(modifiers); + if (inputInfo === null) { return; } - this._searchAtFromMouse(e.clientX, e.clientY); + const {index, empty} = inputInfo; + this._searchAtFromMouse(e.clientX, e.clientY, index, empty); } _onMouseDown(e) { @@ -276,7 +272,7 @@ class TextScanner extends EventDispatcher { _onClick(e) { if (this._searchOnClick) { - this._searchAt(e.clientX, e.clientY, {cause: 'click'}); + this._searchAt(e.clientX, e.clientY, {cause: 'click', index: -1, empty: false}); } if (this._preventNextClick) { @@ -349,7 +345,7 @@ class TextScanner extends EventDispatcher { return; } - this._searchAt(primaryTouch.clientX, primaryTouch.clientY, {cause: 'touchMove'}); + this._searchAt(primaryTouch.clientX, primaryTouch.clientY, {cause: 'touchMove', index: -1, empty: false}); e.preventDefault(); // Disable scroll } @@ -406,17 +402,6 @@ class TextScanner extends EventDispatcher { ]; } - _isScanningModifierPressed(scanningModifier, mouseEvent) { - switch (scanningModifier) { - case 'alt': return mouseEvent.altKey; - case 'ctrl': return mouseEvent.ctrlKey; - case 'shift': return mouseEvent.shiftKey; - case 'meta': return mouseEvent.metaKey; - case 'none': return true; - default: return false; - } - } - _getTouch(touchList, identifier) { for (const touch of touchList) { if (touch.identifier === identifier) { @@ -498,17 +483,17 @@ class TextScanner extends EventDispatcher { } } - async _searchAtFromMouse(x, y) { + async _searchAtFromMouse(x, y, inputIndex, inputEmpty) { if (this._pendingLookup) { return; } - if (this._modifier === 'none') { + if (inputEmpty) { if (!await this._scanTimerWait()) { // Aborted return; } } - await this._searchAt(x, y, {cause: 'mouse'}); + await this._searchAt(x, y, {cause: 'mouse', index: inputIndex, empty: inputEmpty}); } async _searchAtFromTouchStart(x, y) { @@ -516,7 +501,7 @@ class TextScanner extends EventDispatcher { const textSourceCurrentPrevious = this._textSourceCurrent !== null ? this._textSourceCurrent.clone() : null; - await this._searchAt(x, y, {cause: 'touchStart'}); + await this._searchAt(x, y, {cause: 'touchStart', index: -1, empty: false}); if ( this._textSourceCurrent !== null && @@ -527,4 +512,37 @@ class TextScanner extends EventDispatcher { this._preventNextMouseDown = true; } } + + _getMatchingInputGroup(modifiers) { + let fallback = null; + for (let i = 0, ii = this._inputs.length; i < ii; ++i) { + const input = this._inputs[i]; + const {include, exclude} = input; + if (this._setHasAll(modifiers, include) && (exclude.length === 0 || !this._setHasAll(modifiers, exclude))) { + if (include.length > 0) { + return {index: i, empty: false, input}; + } else if (fallback === null) { + fallback = {index: i, empty: true, input}; + } + } + } + return fallback; + } + + _setHasAll(set, values) { + for (const value of values) { + if (!set.has(value)) { + return false; + } + } + return true; + } + + _getInputArray(value) { + return ( + typeof value === 'string' ? + value.split(/[,;\s]+/).map((v) => v.trim().toLowerCase()).filter((v) => v.length > 0) : + [] + ); + } } -- cgit v1.2.3