Pointer events support (#819)
* Add option pointerEventsEnabled * Add _pointerEventsEnabled option to TextScanner * Add additional options * Mouse pointer events * Touch pointer events * Pen pointer events
This commit is contained in:
parent
efd0de6bc0
commit
5b49cf4398
@ -320,6 +320,7 @@
|
|||||||
"required": [
|
"required": [
|
||||||
"inputs",
|
"inputs",
|
||||||
"touchInputEnabled",
|
"touchInputEnabled",
|
||||||
|
"pointerEventsEnabled",
|
||||||
"selectText",
|
"selectText",
|
||||||
"alphanumeric",
|
"alphanumeric",
|
||||||
"autoHideResults",
|
"autoHideResults",
|
||||||
@ -430,6 +431,10 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true
|
"default": true
|
||||||
},
|
},
|
||||||
|
"pointerEventsEnabled": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
"selectText": {
|
"selectText": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": true
|
"default": true
|
||||||
|
@ -505,6 +505,7 @@ class OptionsUtil {
|
|||||||
// Updated handlebars templates to include "clipboard-image" definition.
|
// Updated handlebars templates to include "clipboard-image" definition.
|
||||||
// Added hideDelay.
|
// Added hideDelay.
|
||||||
// Added inputs to profileOptions.scanning.
|
// Added inputs to profileOptions.scanning.
|
||||||
|
// Added pointerEventsEnabled to profileOptions.scanning.
|
||||||
for (const {conditionGroups} of options.profiles) {
|
for (const {conditionGroups} of options.profiles) {
|
||||||
for (const {conditions} of conditionGroups) {
|
for (const {conditions} of conditionGroups) {
|
||||||
for (const condition of conditions) {
|
for (const condition of conditions) {
|
||||||
@ -526,6 +527,7 @@ class OptionsUtil {
|
|||||||
for (const {options: profileOptions} of options.profiles) {
|
for (const {options: profileOptions} of options.profiles) {
|
||||||
profileOptions.general.usePopupWindow = false;
|
profileOptions.general.usePopupWindow = false;
|
||||||
profileOptions.scanning.hideDelay = 0;
|
profileOptions.scanning.hideDelay = 0;
|
||||||
|
profileOptions.scanning.pointerEventsEnabled = false;
|
||||||
|
|
||||||
const {modifier, middleMouse, touchInputEnabled} = profileOptions.scanning;
|
const {modifier, middleMouse, touchInputEnabled} = profileOptions.scanning;
|
||||||
const scanningInputs = [];
|
const scanningInputs = [];
|
||||||
|
@ -406,6 +406,10 @@
|
|||||||
<label><input type="checkbox" id="touch-input-enabled" data-setting="scanning.touchInputEnabled"> Touch input enabled</label>
|
<label><input type="checkbox" id="touch-input-enabled" data-setting="scanning.touchInputEnabled"> Touch input enabled</label>
|
||||||
</div>
|
</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">
|
<div class="checkbox options-advanced">
|
||||||
<label><input type="checkbox" id="deep-dom-scan" data-setting="scanning.deepDomScan"> Deep content scan</label>
|
<label><input type="checkbox" id="deep-dom-scan" data-setting="scanning.deepDomScan"> Deep content scan</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -326,6 +326,7 @@ class Frontend {
|
|||||||
selectText: scanningOptions.selectText,
|
selectText: scanningOptions.selectText,
|
||||||
delay: scanningOptions.delay,
|
delay: scanningOptions.delay,
|
||||||
touchInputEnabled: scanningOptions.touchInputEnabled,
|
touchInputEnabled: scanningOptions.touchInputEnabled,
|
||||||
|
pointerEventsEnabled: scanningOptions.pointerEventsEnabled,
|
||||||
scanLength: scanningOptions.length,
|
scanLength: scanningOptions.length,
|
||||||
sentenceExtent: options.anki.sentenceExt,
|
sentenceExtent: options.anki.sentenceExt,
|
||||||
layoutAwareScan: scanningOptions.layoutAwareScan
|
layoutAwareScan: scanningOptions.layoutAwareScan
|
||||||
|
@ -252,6 +252,7 @@ class Display extends EventDispatcher {
|
|||||||
selectText: scanning.selectText,
|
selectText: scanning.selectText,
|
||||||
delay: scanning.delay,
|
delay: scanning.delay,
|
||||||
touchInputEnabled: scanning.touchInputEnabled,
|
touchInputEnabled: scanning.touchInputEnabled,
|
||||||
|
pointerEventsEnabled: scanning.pointerEventsEnabled,
|
||||||
scanLength: scanning.length,
|
scanLength: scanning.length,
|
||||||
sentenceExtent: options.anki.sentenceExt,
|
sentenceExtent: options.anki.sentenceExt,
|
||||||
layoutAwareScan: scanning.layoutAwareScan
|
layoutAwareScan: scanning.layoutAwareScan
|
||||||
|
@ -45,6 +45,7 @@ class TextScanner extends EventDispatcher {
|
|||||||
this._selectText = false;
|
this._selectText = false;
|
||||||
this._delay = 0;
|
this._delay = 0;
|
||||||
this._touchInputEnabled = false;
|
this._touchInputEnabled = false;
|
||||||
|
this._pointerEventsEnabled = false;
|
||||||
this._scanLength = 1;
|
this._scanLength = 1;
|
||||||
this._sentenceExtent = 1;
|
this._sentenceExtent = 1;
|
||||||
this._layoutAwareScan = false;
|
this._layoutAwareScan = false;
|
||||||
@ -59,6 +60,8 @@ class TextScanner extends EventDispatcher {
|
|||||||
this._preventNextMouseDown = false;
|
this._preventNextMouseDown = false;
|
||||||
this._preventNextClick = false;
|
this._preventNextClick = false;
|
||||||
this._preventScroll = false;
|
this._preventScroll = false;
|
||||||
|
this._penPointerPressed = false;
|
||||||
|
this._penPointerReleased = false;
|
||||||
|
|
||||||
this._canClearSelection = true;
|
this._canClearSelection = true;
|
||||||
}
|
}
|
||||||
@ -96,6 +99,8 @@ class TextScanner extends EventDispatcher {
|
|||||||
this._preventNextMouseDown = false;
|
this._preventNextMouseDown = false;
|
||||||
this._preventNextClick = false;
|
this._preventNextClick = false;
|
||||||
this._preventScroll = false;
|
this._preventScroll = false;
|
||||||
|
this._penPointerPressed = false;
|
||||||
|
this._penPointerReleased = false;
|
||||||
|
|
||||||
this._enabledValue = value;
|
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)) {
|
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),
|
include: this._getInputArray(include),
|
||||||
exclude: this._getInputArray(exclude),
|
exclude: this._getInputArray(exclude),
|
||||||
types: this._getInputTypeSet(types)
|
types: this._getInputTypeSet(types),
|
||||||
|
options: {scanOnPenHover, scanOnPenPress, scanOnPenRelease}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
if (typeof deepContentScan === 'boolean') {
|
if (typeof deepContentScan === 'boolean') {
|
||||||
@ -126,6 +137,9 @@ class TextScanner extends EventDispatcher {
|
|||||||
if (typeof touchInputEnabled === 'boolean') {
|
if (typeof touchInputEnabled === 'boolean') {
|
||||||
this._touchInputEnabled = touchInputEnabled;
|
this._touchInputEnabled = touchInputEnabled;
|
||||||
}
|
}
|
||||||
|
if (typeof pointerEventsEnabled === 'boolean') {
|
||||||
|
this._pointerEventsEnabled = pointerEventsEnabled;
|
||||||
|
}
|
||||||
if (typeof scanLength === 'number') {
|
if (typeof scanLength === 'number') {
|
||||||
this._scanLength = scanLength;
|
this._scanLength = scanLength;
|
||||||
}
|
}
|
||||||
@ -374,6 +388,162 @@ class TextScanner extends EventDispatcher {
|
|||||||
e.preventDefault(); // Disable scroll
|
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() {
|
async _scanTimerWait() {
|
||||||
const delay = this._delay;
|
const delay = this._delay;
|
||||||
const promise = promiseTimeout(delay, true);
|
const promise = promiseTimeout(delay, true);
|
||||||
@ -394,17 +564,41 @@ class TextScanner extends EventDispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_arePointerEventsSupported() {
|
||||||
|
return (this._pointerEventsEnabled && typeof PointerEvent !== 'undefined');
|
||||||
|
}
|
||||||
|
|
||||||
_hookEvents() {
|
_hookEvents() {
|
||||||
const eventListenerInfos = this._getMouseEventListeners();
|
let eventListenerInfos;
|
||||||
|
if (this._arePointerEventsSupported()) {
|
||||||
|
eventListenerInfos = this._getPointerEventListeners();
|
||||||
|
} else {
|
||||||
|
eventListenerInfos = this._getMouseEventListeners();
|
||||||
if (this._touchInputEnabled) {
|
if (this._touchInputEnabled) {
|
||||||
eventListenerInfos.push(...this._getTouchEventListeners());
|
eventListenerInfos.push(...this._getTouchEventListeners());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const [node, type, listener, options] of eventListenerInfos) {
|
for (const [node, type, listener, options] of eventListenerInfos) {
|
||||||
this._eventListeners.addEventListener(node, type, listener, options);
|
this._eventListeners.addEventListener(node, type, listener, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_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() {
|
_getMouseEventListeners() {
|
||||||
return [
|
return [
|
||||||
[this._node, 'mousedown', this._onMouseDown.bind(this)],
|
[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) {
|
_getMatchingInputGroupFromEvent(event, type) {
|
||||||
const modifiers = DocumentUtil.getActiveModifiersAndButtons(event);
|
const modifiers = DocumentUtil.getActiveModifiersAndButtons(event);
|
||||||
this.trigger('activeModifiersChanged', {modifiers});
|
this.trigger('activeModifiersChanged', {modifiers});
|
||||||
|
Loading…
Reference in New Issue
Block a user