diff options
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/bg/data/options-schema.json | 5 | ||||
| -rw-r--r-- | ext/bg/js/options.js | 2 | ||||
| -rw-r--r-- | ext/bg/settings.html | 4 | ||||
| -rw-r--r-- | ext/fg/js/frontend.js | 1 | ||||
| -rw-r--r-- | ext/mixed/js/display.js | 1 | ||||
| -rw-r--r-- | ext/mixed/js/text-scanner.js | 234 | 
6 files changed, 241 insertions, 6 deletions
| diff --git a/ext/bg/data/options-schema.json b/ext/bg/data/options-schema.json index 9cc8837d..240305b5 100644 --- a/ext/bg/data/options-schema.json +++ b/ext/bg/data/options-schema.json @@ -320,6 +320,7 @@                                  "required": [                                      "inputs",                                      "touchInputEnabled", +                                    "pointerEventsEnabled",                                      "selectText",                                      "alphanumeric",                                      "autoHideResults", @@ -430,6 +431,10 @@                                          "type": "boolean",                                          "default": true                                      }, +                                    "pointerEventsEnabled": { +                                        "type": "boolean", +                                        "default": false +                                    },                                      "selectText": {                                          "type": "boolean",                                          "default": true diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index 3470da58..89538b3e 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -505,6 +505,7 @@ class OptionsUtil {          //  Updated handlebars templates to include "clipboard-image" definition.          //  Added hideDelay.          //  Added inputs to profileOptions.scanning. +        //  Added pointerEventsEnabled to profileOptions.scanning.          for (const {conditionGroups} of options.profiles) {              for (const {conditions} of conditionGroups) {                  for (const condition of conditions) { @@ -526,6 +527,7 @@ class OptionsUtil {          for (const {options: profileOptions} of options.profiles) {              profileOptions.general.usePopupWindow = false;              profileOptions.scanning.hideDelay = 0; +            profileOptions.scanning.pointerEventsEnabled = false;              const {modifier, middleMouse, touchInputEnabled} = profileOptions.scanning;              const scanningInputs = []; diff --git a/ext/bg/settings.html b/ext/bg/settings.html index cc6f93e0..cc209c8a 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -407,6 +407,10 @@                  </div>                  <div class="checkbox options-advanced"> +                    <label><input type="checkbox" data-setting="scanning.pointerEventsEnabled"> Pointer events input enabled</label> +                </div> + +                <div class="checkbox options-advanced">                      <label><input type="checkbox" id="deep-dom-scan" data-setting="scanning.deepDomScan"> Deep content scan</label>                  </div> diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 6b0b50a6..da620136 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -326,6 +326,7 @@ class Frontend {              selectText: scanningOptions.selectText,              delay: scanningOptions.delay,              touchInputEnabled: scanningOptions.touchInputEnabled, +            pointerEventsEnabled: scanningOptions.pointerEventsEnabled,              scanLength: scanningOptions.length,              sentenceExtent: options.anki.sentenceExt,              layoutAwareScan: scanningOptions.layoutAwareScan diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 92e27faa..ef56f4aa 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -252,6 +252,7 @@ class Display extends EventDispatcher {                  selectText: scanning.selectText,                  delay: scanning.delay,                  touchInputEnabled: scanning.touchInputEnabled, +                pointerEventsEnabled: scanning.pointerEventsEnabled,                  scanLength: scanning.length,                  sentenceExtent: options.anki.sentenceExt,                  layoutAwareScan: scanning.layoutAwareScan diff --git a/ext/mixed/js/text-scanner.js b/ext/mixed/js/text-scanner.js index d2383af4..c2016807 100644 --- a/ext/mixed/js/text-scanner.js +++ b/ext/mixed/js/text-scanner.js @@ -45,6 +45,7 @@ class TextScanner extends EventDispatcher {          this._selectText = false;          this._delay = 0;          this._touchInputEnabled = false; +        this._pointerEventsEnabled = false;          this._scanLength = 1;          this._sentenceExtent = 1;          this._layoutAwareScan = false; @@ -59,6 +60,8 @@ class TextScanner extends EventDispatcher {          this._preventNextMouseDown = false;          this._preventNextClick = false;          this._preventScroll = false; +        this._penPointerPressed = false; +        this._penPointerReleased = false;          this._canClearSelection = true;      } @@ -96,6 +99,8 @@ class TextScanner extends EventDispatcher {          this._preventNextMouseDown = false;          this._preventNextClick = false;          this._preventScroll = false; +        this._penPointerPressed = false; +        this._penPointerReleased = false;          this._enabledValue = value; @@ -106,12 +111,18 @@ class TextScanner extends EventDispatcher {          }      } -    setOptions({inputs, deepContentScan, selectText, delay, touchInputEnabled, scanLength, sentenceExtent, layoutAwareScan}) { +    setOptions({inputs, deepContentScan, selectText, delay, touchInputEnabled, pointerEventsEnabled, scanLength, sentenceExtent, layoutAwareScan}) {          if (Array.isArray(inputs)) { -            this._inputs = inputs.map(({include, exclude, types}) => ({ +            this._inputs = inputs.map(({ +                include, +                exclude, +                types, +                options: {scanOnPenHover, scanOnPenPress, scanOnPenRelease} +            }) => ({                  include: this._getInputArray(include),                  exclude: this._getInputArray(exclude), -                types: this._getInputTypeSet(types) +                types: this._getInputTypeSet(types), +                options: {scanOnPenHover, scanOnPenPress, scanOnPenRelease}              }));          }          if (typeof deepContentScan === 'boolean') { @@ -126,6 +137,9 @@ class TextScanner extends EventDispatcher {          if (typeof touchInputEnabled === 'boolean') {              this._touchInputEnabled = touchInputEnabled;          } +        if (typeof pointerEventsEnabled === 'boolean') { +            this._pointerEventsEnabled = pointerEventsEnabled; +        }          if (typeof scanLength === 'number') {              this._scanLength = scanLength;          } @@ -374,6 +388,162 @@ class TextScanner extends EventDispatcher {          e.preventDefault(); // Disable scroll      } +    _onPointerOver(e) { +        if (!e.isPrimary) { return; } +        switch (e.pointerType) { +            case 'mouse': return this._onMousePointerOver(e); +            case 'touch': return this._onTouchPointerOver(e); +            case 'pen': return this._onPenPointerOver(e); +        } +    } + +    _onPointerDown(e) { +        if (!e.isPrimary) { return; } +        switch (e.pointerType) { +            case 'mouse': return this._onMousePointerDown(e); +            case 'touch': return this._onTouchPointerDown(e); +            case 'pen': return this._onPenPointerDown(e); +        } +    } + +    _onPointerMove(e) { +        if (!e.isPrimary) { return; } +        switch (e.pointerType) { +            case 'mouse': return this._onMousePointerMove(e); +            case 'touch': return this._onTouchPointerMove(e); +            case 'pen': return this._onPenPointerMove(e); +        } +    } + +    _onPointerUp(e) { +        if (!e.isPrimary) { return; } +        switch (e.pointerType) { +            case 'mouse': return this._onMousePointerUp(e); +            case 'touch': return this._onTouchPointerUp(e); +            case 'pen': return this._onPenPointerUp(e); +        } +    } + +    _onPointerCancel(e) { +        if (!e.isPrimary) { return; } +        switch (e.pointerType) { +            case 'mouse': return this._onMousePointerCancel(e); +            case 'touch': return this._onTouchPointerCancel(e); +            case 'pen': return this._onPenPointerCancel(e); +        } +    } + +    _onPointerOut(e) { +        if (!e.isPrimary) { return; } +        switch (e.pointerType) { +            case 'mouse': return this._onMousePointerOut(e); +            case 'touch': return this._onTouchPointerOut(e); +            case 'pen': return this._onPenPointerOut(e); +        } +    } + +    _onMousePointerOver(e) { +        return this._onMouseOver(e); +    } + +    _onMousePointerDown(e) { +        return this._onMouseDown(e); +    } + +    _onMousePointerMove(e) { +        return this._onMouseMove(e); +    } + +    _onMousePointerUp() { +        // NOP +    } + +    _onMousePointerCancel(e) { +        return this._onMouseOut(e); +    } + +    _onMousePointerOut(e) { +        return this._onMouseOut(e); +    } + +    _onTouchPointerOver() { +        // NOP +    } + +    _onTouchPointerDown(e) { +        const {clientX, clientY, pointerId} = e; +        return this._onPrimaryTouchStart(e, clientX, clientY, pointerId); +    } + +    _onTouchPointerMove(e) { +        if (!this._preventScroll || !e.cancelable) { +            return; +        } + +        const inputInfo = this._getMatchingInputGroupFromEvent(e, 'touch'); +        if (inputInfo === null) { return; } + +        const {index, empty} = inputInfo; +        this._searchAt(e.clientX, e.clientY, {type: 'touch', cause: 'touchMove', index, empty}); +    } + +    _onTouchPointerUp() { +        return this._onPrimaryTouchEnd(); +    } + +    _onTouchPointerCancel() { +        return this._onPrimaryTouchEnd(); +    } + +    _onTouchPointerOut() { +        // NOP +    } + +    _onTouchMovePreventScroll(e) { +        if (!this._preventScroll) { return; } + +        if (e.cancelable) { +            e.preventDefault(); +        } else { +            this._preventScroll = false; +        } +    } + +    _onPenPointerOver(e) { +        this._penPointerPressed = false; +        this._penPointerReleased = false; +        this._searchAtFromPen(e, e.clientX, e.clientY, 'pointerOver', false); +    } + +    _onPenPointerDown(e) { +        this._penPointerPressed = true; +        this._searchAtFromPen(e, e.clientX, e.clientY, 'pointerDown', true); +    } + +    _onPenPointerMove(e) { +        if (this._penPointerPressed && (!this._preventScroll || !e.cancelable)) { return; } +        this._searchAtFromPen(e, e.clientX, e.clientY, 'pointerMove', true); +    } + +    _onPenPointerUp() { +        this._penPointerPressed = false; +        this._penPointerReleased = true; +        this._preventScroll = false; +    } + +    _onPenPointerCancel(e) { +        this._onPenPointerOut(e); +    } + +    _onPenPointerOut() { +        this._penPointerPressed = false; +        this._penPointerReleased = false; +        this._preventScroll = false; +        this._preventNextContextMenu = false; +        this._preventNextMouseDown = false; +        this._preventNextClick = false; +    } +      async _scanTimerWait() {          const delay = this._delay;          const promise = promiseTimeout(delay, true); @@ -394,10 +564,19 @@ class TextScanner extends EventDispatcher {          }      } +    _arePointerEventsSupported() { +        return (this._pointerEventsEnabled && typeof PointerEvent !== 'undefined'); +    } +      _hookEvents() { -        const eventListenerInfos = this._getMouseEventListeners(); -        if (this._touchInputEnabled) { -            eventListenerInfos.push(...this._getTouchEventListeners()); +        let eventListenerInfos; +        if (this._arePointerEventsSupported()) { +            eventListenerInfos = this._getPointerEventListeners(); +        } else { +            eventListenerInfos = this._getMouseEventListeners(); +            if (this._touchInputEnabled) { +                eventListenerInfos.push(...this._getTouchEventListeners()); +            }          }          for (const [node, type, listener, options] of eventListenerInfos) { @@ -405,6 +584,21 @@ class TextScanner extends EventDispatcher {          }      } +    _getPointerEventListeners() { +        return [ +            [this._node, 'pointerover', this._onPointerOver.bind(this)], +            [this._node, 'pointerdown', this._onPointerDown.bind(this)], +            [this._node, 'pointermove', this._onPointerMove.bind(this)], +            [this._node, 'pointerup', this._onPointerUp.bind(this)], +            [this._node, 'pointercancel', this._onPointerCancel.bind(this)], +            [this._node, 'pointerout', this._onPointerOut.bind(this)], +            [this._node, 'touchmove', this._onTouchMovePreventScroll.bind(this), {passive: false}], +            [this._node, 'mousedown', this._onMouseDown.bind(this)], +            [this._node, 'click', this._onClick.bind(this)], +            [this._node, 'auxclick', this._onAuxClick.bind(this)] +        ]; +    } +      _getMouseEventListeners() {          return [              [this._node, 'mousedown', this._onMouseDown.bind(this)], @@ -543,6 +737,34 @@ class TextScanner extends EventDispatcher {          }      } +    async _searchAtFromPen(e, x, y, cause, prevent) { +        if (this._pendingLookup) { return; } + +        const type = 'pen'; +        const inputInfo = this._getMatchingInputGroupFromEvent(e, type); +        if (inputInfo === null) { return; } + +        const {index, empty, input: {options}} = inputInfo; +        if ( +            (!options.scanOnPenRelease && this._penPointerReleased) || +            !(this._penPointerPressed ? options.scanOnPenPress : options.scanOnPenHover) +        ) { +            return; +        } + +        await this._searchAt(x, y, {type, cause, index, empty}); + +        if ( +            prevent && +            this._textSourceCurrent !== null +        ) { +            this._preventScroll = true; +            this._preventNextContextMenu = true; +            this._preventNextMouseDown = true; +            this._preventNextClick = true; +        } +    } +      _getMatchingInputGroupFromEvent(event, type) {          const modifiers = DocumentUtil.getActiveModifiersAndButtons(event);          this.trigger('activeModifiersChanged', {modifiers}); |