From c966d9b1ebb12386ac876d93f377fe3a470c6976 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 3 Jun 2022 17:11:32 -0400 Subject: Touch and pen input updates (#2172) * Remove unnecessary return * Move touch start input filtering * Refactor * Add scanOnTouchPress * Add preventPenScrolling * Rename scanOnPenPress to scanOnPenMove * Rename scanOnPenRelease to scanOnPenReleaseHover * Simplify * Refactor _searchAtFromPen early exit * Merge _penPointerPressed and _penPointerReleased into a single variable * Add more options * Simplify pen pointer coordinates * Implement scanOnPenPress and scanOnPenRelease * Implement scanOnTouchRelease * Fix tests * Don't search on touch cancel * Cancel touch if the touch action is used for scrolling or other gestures * Fix incorrect scroll prevention options being used * Organize options * Fix typos --- ext/css/settings.css | 13 ++- ext/data/schemas/options-schema.json | 42 ++++++- ext/js/data/options-util.js | 16 +++ ext/js/display/display.js | 7 +- ext/js/language/text-scanner.js | 145 +++++++++++++++--------- ext/js/pages/settings/scan-inputs-controller.js | 7 +- ext/settings.html | 36 +++++- test/test-options-util.js | 21 +++- 8 files changed, 216 insertions(+), 71 deletions(-) diff --git a/ext/css/settings.css b/ext/css/settings.css index dc8132be..6f1afa00 100644 --- a/ext/css/settings.css +++ b/ext/css/settings.css @@ -1714,9 +1714,12 @@ code.anki-field-marker { .scan-input-prefix-cell[data-property=search-options] { grid-area: 4/2/5/3; } -.scan-input-prefix-cell[data-property=touch-pen-options] { +.scan-input-prefix-cell[data-property=touch-options] { grid-area: 5/2/6/3; } +.scan-input-prefix-cell[data-property=pen-options] { + grid-area: 6/2/7/3; +} .scan-input-content-cell[data-property=include] { grid-area: 1/3/2/4; } @@ -1735,12 +1738,18 @@ code.anki-field-marker { flex-flow: row wrap; align-items: center; } -.scan-input-content-cell[data-property=touch-pen-options] { +.scan-input-content-cell[data-property=touch-options] { grid-area: 5/3/6/4; display: flex; flex-flow: column nowrap; align-items: flex-start; } +.scan-input-content-cell[data-property=pen-options] { + grid-area: 6/3/7/4; + display: flex; + flex-flow: column nowrap; + align-items: flex-start; +} .scan-input-options-cell { padding: 0.25em 0; align-self: start; diff --git a/ext/data/schemas/options-schema.json b/ext/data/schemas/options-schema.json index 279be153..d7e3b5f4 100644 --- a/ext/data/schemas/options-schema.json +++ b/ext/data/schemas/options-schema.json @@ -457,10 +457,15 @@ "searchTerms": true, "searchKanji": true, "scanOnTouchMove": true, + "scanOnTouchPress": true, + "scanOnTouchRelease": false, + "scanOnPenMove": true, "scanOnPenHover": true, + "scanOnPenReleaseHover": false, "scanOnPenPress": true, "scanOnPenRelease": false, - "preventTouchScrolling": false + "preventTouchScrolling": false, + "preventPenScrolling": false } }, { @@ -476,10 +481,15 @@ "searchTerms": true, "searchKanji": true, "scanOnTouchMove": true, + "scanOnTouchPress": true, + "scanOnTouchRelease": false, + "scanOnPenMove": true, "scanOnPenHover": true, + "scanOnPenReleaseHover": false, "scanOnPenPress": true, "scanOnPenRelease": false, - "preventTouchScrolling": true + "preventTouchScrolling": true, + "preventPenScrolling": true } } ], @@ -528,10 +538,12 @@ "searchTerms", "searchKanji", "scanOnTouchMove", + "scanOnTouchPress", + "scanOnPenMove", "scanOnPenHover", - "scanOnPenPress", - "scanOnPenRelease", - "preventTouchScrolling" + "scanOnPenReleaseHover", + "preventTouchScrolling", + "preventPenScrolling" ], "properties": { "showAdvanced": { @@ -550,10 +562,26 @@ "type": "boolean", "default": true }, + "scanOnTouchPress": { + "type": "boolean", + "default": true + }, + "scanOnTouchRelease": { + "type": "boolean", + "default": false + }, + "scanOnPenMove": { + "type": "boolean", + "default": true + }, "scanOnPenHover": { "type": "boolean", "default": true }, + "scanOnPenReleaseHover": { + "type": "boolean", + "default": false + }, "scanOnPenPress": { "type": "boolean", "default": true @@ -565,6 +593,10 @@ "preventTouchScrolling": { "type": "boolean", "default": true + }, + "preventPenScrolling": { + "type": "boolean", + "default": true } } } diff --git a/ext/js/data/options-util.js b/ext/js/data/options-util.js index f19094df..a163580f 100644 --- a/ext/js/data/options-util.js +++ b/ext/js/data/options-util.js @@ -953,9 +953,25 @@ class OptionsUtil { // Version 19 changes: // Added anki.noteGuiMode. // Added anki.apiKey. + // Renamed scanning.inputs[].options.scanOnPenPress to scanOnPenMove. + // Renamed scanning.inputs[].options.scanOnPenRelease to scanOnPenReleaseHover. + // Added scanning.inputs[].options.scanOnTouchPress. + // Added scanning.inputs[].options.scanOnTouchRelease. + // Added scanning.inputs[].options.scanOnPenPress. + // Added scanning.inputs[].options.scanOnPenRelease. + // Added scanning.inputs[].options.preventPenScrolling. for (const profile of options.profiles) { profile.options.anki.noteGuiMode = 'browse'; profile.options.anki.apiKey = ''; + for (const input of profile.options.scanning.inputs) { + input.options.scanOnPenMove = input.options.scanOnPenPress; + input.options.scanOnPenReleaseHover = input.options.scanOnPenRelease; + input.options.scanOnTouchPress = true; + input.options.scanOnTouchRelease = false; + input.options.scanOnPenPress = input.options.scanOnPenMove; + input.options.scanOnPenRelease = false; + input.options.preventPenScrolling = input.options.preventTouchScrolling; + } } return options; } diff --git a/ext/js/display/display.js b/ext/js/display/display.js index f9153420..923c9dd2 100644 --- a/ext/js/display/display.js +++ b/ext/js/display/display.js @@ -1516,10 +1516,15 @@ class Display extends EventDispatcher { searchTerms: true, searchKanji: true, scanOnTouchMove: false, + scanOnTouchPress: false, + scanOnTouchRelease: false, + scanOnPenMove: false, scanOnPenHover: false, + scanOnPenReleaseHover: false, scanOnPenPress: false, scanOnPenRelease: false, - preventTouchScrolling: false + preventTouchScrolling: false, + preventPenScrolling: false } }], deepContentScan: scanningOptions.deepDomScan, diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js index 36091805..978f3d36 100644 --- a/ext/js/language/text-scanner.js +++ b/ext/js/language/text-scanner.js @@ -83,8 +83,7 @@ class TextScanner extends EventDispatcher { this._preventNextMouseDown = false; this._preventNextClick = false; this._preventScroll = false; - this._penPointerPressed = false; - this._penPointerReleased = false; + this._penPointerState = 0; // 0 = not active; 1 = hovering; 2 = touching; 3 = hovering after touching this._pointerIdTypeMap = new Map(); this._canClearSelection = true; @@ -135,8 +134,7 @@ class TextScanner extends EventDispatcher { this._preventNextMouseDown = false; this._preventNextClick = false; this._preventScroll = false; - this._penPointerPressed = false; - this._penPointerReleased = false; + this._penPointerState = 0; this._pointerIdTypeMap.clear(); this._enabledValue = value; @@ -168,10 +166,15 @@ class TextScanner extends EventDispatcher { searchTerms, searchKanji, scanOnTouchMove, + scanOnTouchPress, + scanOnTouchRelease, + scanOnPenMove, scanOnPenHover, + scanOnPenReleaseHover, scanOnPenPress, scanOnPenRelease, - preventTouchScrolling + preventTouchScrolling, + preventPenScrolling } }) => ({ include: this._getInputArray(include), @@ -181,10 +184,15 @@ class TextScanner extends EventDispatcher { searchTerms, searchKanji, scanOnTouchMove, + scanOnTouchPress, + scanOnTouchRelease, + scanOnPenMove, scanOnPenHover, + scanOnPenReleaseHover, scanOnPenPress, scanOnPenRelease, - preventTouchScrolling + preventTouchScrolling, + preventPenScrolling } })); } @@ -515,41 +523,60 @@ class TextScanner extends EventDispatcher { this._primaryTouchIdentifier = identifier; - this._searchAtFromTouchStart(e, x, y); + if (this._pendingLookup) { return; } + + const inputInfo = this._getMatchingInputGroupFromEvent('touch', 'touchStart', e); + if (inputInfo === null || !inputInfo.input.options.scanOnTouchPress) { return; } + + this._searchAtFromTouchStart(x, y, inputInfo); } _onTouchEnd(e) { - if ( - this._primaryTouchIdentifier === null || - this._getTouch(e.changedTouches, this._primaryTouchIdentifier) === null - ) { - return; - } + if (this._primaryTouchIdentifier === null) { return; } + + const primaryTouch = this._getTouch(e.changedTouches, this._primaryTouchIdentifier); + if (primaryTouch === null) { return; } - this._onPrimaryTouchEnd(); + const {clientX, clientY} = primaryTouch; + this._onPrimaryTouchEnd(e, clientX, clientY, true); } - _onPrimaryTouchEnd() { + _onPrimaryTouchEnd(e, x, y, allowSearch) { this._primaryTouchIdentifier = null; this._preventScroll = false; this._preventNextClick = false; // Don't revert context menu and mouse down prevention, since these events can occur after the touch has ended. // I.e. this._preventNextContextMenu and this._preventNextMouseDown should not be assigned to false. + + if (!allowSearch) { return; } + + const inputInfo = this._getMatchingInputGroupFromEvent('touch', 'touchEnd', e); + if (inputInfo === null || !inputInfo.input.options.scanOnTouchRelease) { return; } + + this._searchAtFromTouchEnd(x, y, inputInfo); } _onTouchCancel(e) { - this._onTouchEnd(e); + if (this._primaryTouchIdentifier === null) { return; } + + const primaryTouch = this._getTouch(e.changedTouches, this._primaryTouchIdentifier); + if (primaryTouch === null) { return; } + + this._onPrimaryTouchEnd(e, 0, 0, false); } _onTouchMove(e) { - if (!this._preventScroll || !e.cancelable || this._primaryTouchIdentifier === null) { + if (this._primaryTouchIdentifier === null) { return; } + + if (!e.cancelable) { + this._onPrimaryTouchEnd(e, 0, 0, false); return; } + if (!this._preventScroll) { return; } + const primaryTouch = this._getTouch(e.changedTouches, this._primaryTouchIdentifier); - if (primaryTouch === null) { - return; - } + if (primaryTouch === null) { return; } const inputInfo = this._getMatchingInputGroupFromEvent('touch', 'touchMove', e); if (inputInfo === null) { return; } @@ -652,7 +679,7 @@ class TextScanner extends EventDispatcher { _onTouchPointerDown(e) { const {clientX, clientY, pointerId} = e; - return this._onPrimaryTouchStart(e, clientX, clientY, pointerId); + this._onPrimaryTouchStart(e, clientX, clientY, pointerId); } _onTouchPointerMove(e) { @@ -666,12 +693,13 @@ class TextScanner extends EventDispatcher { this._searchAt(e.clientX, e.clientY, inputInfo); } - _onTouchPointerUp() { - return this._onPrimaryTouchEnd(); + _onTouchPointerUp(e) { + const {clientX, clientY} = e; + return this._onPrimaryTouchEnd(e, clientX, clientY, true); } - _onTouchPointerCancel() { - return this._onPrimaryTouchEnd(); + _onTouchPointerCancel(e) { + return this._onPrimaryTouchEnd(e, 0, 0, false); } _onTouchPointerOut() { @@ -689,25 +717,24 @@ class TextScanner extends EventDispatcher { } _onPenPointerOver(e) { - this._penPointerPressed = false; - this._penPointerReleased = false; - this._searchAtFromPen(e, e.clientX, e.clientY, 'pointerOver', false); + this._penPointerState = 1; + this._searchAtFromPen(e, 'pointerOver', false); } _onPenPointerDown(e) { - this._penPointerPressed = true; - this._searchAtFromPen(e, e.clientX, e.clientY, 'pointerDown', true); + this._penPointerState = 2; + this._searchAtFromPen(e, 'pointerDown', true); } _onPenPointerMove(e) { - if (this._penPointerPressed && (!this._preventScroll || !e.cancelable)) { return; } - this._searchAtFromPen(e, e.clientX, e.clientY, 'pointerMove', true); + if (this._penPointerState === 2 && (!this._preventScroll || !e.cancelable)) { return; } + this._searchAtFromPen(e, 'pointerMove', true); } - _onPenPointerUp() { - this._penPointerPressed = false; - this._penPointerReleased = true; + _onPenPointerUp(e) { + this._penPointerState = 3; this._preventScroll = false; + this._searchAtFromPen(e, 'pointerUp', false); } _onPenPointerCancel(e) { @@ -715,8 +742,7 @@ class TextScanner extends EventDispatcher { } _onPenPointerOut() { - this._penPointerPressed = false; - this._penPointerReleased = false; + this._penPointerState = 0; this._preventScroll = false; this._preventNextContextMenu = false; this._preventNextMouseDown = false; @@ -953,12 +979,7 @@ class TextScanner extends EventDispatcher { await this._searchAt(x, y, inputInfo); } - async _searchAtFromTouchStart(e, x, y) { - if (this._pendingLookup) { return; } - - const inputInfo = this._getMatchingInputGroupFromEvent('touch', 'touchStart', e); - if (inputInfo === null) { return; } - + async _searchAtFromTouchStart(x, y, inputInfo) { const textSourceCurrentPrevious = this._textSourceCurrent !== null ? this._textSourceCurrent.clone() : null; const preventScroll = inputInfo.input.options.preventTouchScrolling; @@ -974,23 +995,22 @@ class TextScanner extends EventDispatcher { } } - async _searchAtFromPen(e, x, y, eventType, prevent) { + async _searchAtFromTouchEnd(x, y, inputInfo) { + await this._searchAt(x, y, inputInfo); + } + + async _searchAtFromPen(e, eventType, prevent) { if (this._pendingLookup) { return; } const inputInfo = this._getMatchingInputGroupFromEvent('pen', eventType, e); if (inputInfo === null) { return; } - const {input: {options}} = inputInfo; - if ( - (!options.scanOnPenRelease && this._penPointerReleased) || - !(this._penPointerPressed ? options.scanOnPenPress : options.scanOnPenHover) - ) { - return; - } + const {options} = inputInfo.input; + if (!this._isPenEventSupported(eventType, options)) { return; } - const preventScroll = inputInfo.input.options.preventTouchScrolling; + const preventScroll = options.preventPenScrolling; - await this._searchAt(x, y, inputInfo); + await this._searchAt(e.clientX, e.clientY, inputInfo); if ( prevent && @@ -1003,6 +1023,25 @@ class TextScanner extends EventDispatcher { } } + _isPenEventSupported(eventType, options) { + switch (eventType) { + case 'pointerDown': + return options.scanOnPenPress; + case 'pointerUp': + return options.scanOnPenRelease; + } + switch (this._penPointerState) { + case 1: // hovering + return options.scanOnPenHover; + case 2: // touching + return options.scanOnPenMove; + case 3: // hovering after touching + return options.scanOnPenReleaseHover; + default: // not active + return false; + } + } + _getMatchingInputGroupFromEvent(pointerType, eventType, event) { const modifiers = DocumentUtil.getActiveModifiersAndButtons(event); const modifierKeys = DocumentUtil.getActiveModifiers(event); diff --git a/ext/js/pages/settings/scan-inputs-controller.js b/ext/js/pages/settings/scan-inputs-controller.js index 7b363b9a..855ccf9a 100644 --- a/ext/js/pages/settings/scan-inputs-controller.js +++ b/ext/js/pages/settings/scan-inputs-controller.js @@ -150,10 +150,15 @@ class ScanInputsController { searchTerms: true, searchKanji: true, scanOnTouchMove: true, + scanOnTouchPress: true, + scanOnTouchRelease: false, + scanOnPenMove: true, scanOnPenHover: true, + scanOnPenReleaseHover: false, scanOnPenPress: true, scanOnPenRelease: false, - preventTouchScrolling: true + preventTouchScrolling: true, + preventPenScrolling: true } }; } diff --git a/ext/settings.html b/ext/settings.html index 64246fd6..ec9dd34b 100644 --- a/ext/settings.html +++ b/ext/settings.html @@ -2934,16 +2934,28 @@ -
Touch & pen:
-
+
Touch options:
+
+ + +
+ +
Pen options:
+
+ + +
diff --git a/test/test-options-util.js b/test/test-options-util.js index c4f9a3a9..16660fd0 100644 --- a/test/test-options-util.js +++ b/test/test-options-util.js @@ -367,10 +367,15 @@ function createProfileOptionsUpdatedTestData1() { searchTerms: true, searchKanji: true, scanOnTouchMove: true, + scanOnTouchPress: true, + scanOnTouchRelease: false, + scanOnPenMove: true, scanOnPenHover: true, + scanOnPenReleaseHover: false, scanOnPenPress: true, scanOnPenRelease: false, - preventTouchScrolling: true + preventTouchScrolling: true, + preventPenScrolling: true } }, { @@ -386,10 +391,15 @@ function createProfileOptionsUpdatedTestData1() { searchTerms: true, searchKanji: true, scanOnTouchMove: true, + scanOnTouchPress: true, + scanOnTouchRelease: false, + scanOnPenMove: true, scanOnPenHover: true, + scanOnPenReleaseHover: false, scanOnPenPress: true, scanOnPenRelease: false, - preventTouchScrolling: true + preventTouchScrolling: true, + preventPenScrolling: true } }, { @@ -405,10 +415,15 @@ function createProfileOptionsUpdatedTestData1() { searchTerms: true, searchKanji: true, scanOnTouchMove: true, + scanOnTouchPress: true, + scanOnTouchRelease: false, + scanOnPenMove: true, scanOnPenHover: true, + scanOnPenReleaseHover: false, scanOnPenPress: true, scanOnPenRelease: false, - preventTouchScrolling: true + preventTouchScrolling: true, + preventPenScrolling: true } } ] -- cgit v1.2.3