diff --git a/ext/js/display/display.js b/ext/js/display/display.js index 44b6a8e1a..f7e2288ab 100644 --- a/ext/js/display/display.js +++ b/ext/js/display/display.js @@ -2014,6 +2014,8 @@ export class Display extends EventDispatcher { getSearchContext: this._getSearchContext.bind(this), searchTerms: true, searchKanji: false, + searchOnClick: true, + searchOnClickOnly: true, textSourceGenerator: this._textSourceGenerator, }); this._contentTextScanner.includeSelector = '.click-scannable,.click-scannable *'; diff --git a/ext/js/display/query-parser.js b/ext/js/display/query-parser.js index e4e502890..fc2a4cade 100644 --- a/ext/js/display/query-parser.js +++ b/ext/js/display/query-parser.js @@ -66,6 +66,7 @@ export class QueryParser extends EventDispatcher { getSearchContext, searchTerms: true, searchKanji: false, + searchOnClick: true, textSourceGenerator, }); /** @type {?(import('../language/ja/japanese-wanakana.js'))} */ diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js index 0c7d93ea1..d415467da 100644 --- a/ext/js/language/text-scanner.js +++ b/ext/js/language/text-scanner.js @@ -39,6 +39,8 @@ export class TextScanner extends EventDispatcher { ignorePoint = null, searchTerms = false, searchKanji = false, + searchOnClick = false, + searchOnClickOnly = false, textSourceGenerator, }) { super(); @@ -56,6 +58,10 @@ export class TextScanner extends EventDispatcher { this._searchTerms = searchTerms; /** @type {boolean} */ this._searchKanji = searchKanji; + /** @type {boolean} */ + this._searchOnClick = searchOnClick; + /** @type {boolean} */ + this._searchOnClickOnly = searchOnClickOnly; /** @type {import('../dom/text-source-generator').TextSourceGenerator} */ this._textSourceGenerator = textSourceGenerator; @@ -658,6 +664,7 @@ export class TextScanner extends EventDispatcher { switch (e.button) { case 0: // Primary + if (this._searchOnClick) { this._resetPreventNextClickScan(); } this._scanTimerClear(); this._triggerClear('mousedown'); break; @@ -691,9 +698,33 @@ export class TextScanner extends EventDispatcher { return false; } + if (this._searchOnClick) { + this._onSearchClick(e); + return; + } + this._onMouseMove(e); } + /** + * @param {MouseEvent} e + */ + _onSearchClick(e) { + const preventNextClickScan = this._preventNextClickScan; + this._preventNextClickScan = false; + if (this._preventNextClickScanTimer !== null) { + clearTimeout(this._preventNextClickScanTimer); + this._preventNextClickScanTimer = null; + } + + if (preventNextClickScan) { return; } + + const modifiers = getActiveModifiersAndButtons(e); + const modifierKeys = getActiveModifiers(e); + const inputInfo = this._createInputInfo(null, 'mouse', 'click', false, modifiers, modifierKeys); + void this._searchAt(e.clientX, e.clientY, inputInfo); + } + /** */ _onAuxClick() { this._preventNextContextMenu = false; @@ -1117,7 +1148,9 @@ export class TextScanner extends EventDispatcher { const capture = true; /** @type {import('event-listener-collection').AddEventListenerArgs[]} */ let eventListenerInfos; - if (this._arePointerEventsSupported()) { + if (this._searchOnClickOnly) { + eventListenerInfos = this._getMouseClickOnlyEventListeners(capture); + } else if (this._arePointerEventsSupported()) { eventListenerInfos = this._getPointerEventListeners(capture); } else { eventListenerInfos = [...this._getMouseEventListeners(capture)]; @@ -1128,6 +1161,9 @@ export class TextScanner extends EventDispatcher { eventListenerInfos.push(...this._getTouchEventListeners(capture)); } } + if (this._searchOnClick) { + eventListenerInfos.push(...this._getMouseClickOnlyEventListeners2(capture)); + } eventListenerInfos.push(this._getSelectionChangeCheckUserSelectionListener()); @@ -1194,6 +1230,35 @@ export class TextScanner extends EventDispatcher { ]; } + /** + * @param {boolean} capture + * @returns {import('event-listener-collection').AddEventListenerArgs[]} + */ + _getMouseClickOnlyEventListeners(capture) { + return [ + [this._node, 'click', this._onClick.bind(this), capture], + ]; + } + + /** + * @param {boolean} capture + * @returns {import('event-listener-collection').AddEventListenerArgs[]} + */ + _getMouseClickOnlyEventListeners2(capture) { + const {documentElement} = document; + /** @type {import('event-listener-collection').AddEventListenerArgs[]} */ + const entries = [ + [document, 'selectionchange', this._onSelectionChange.bind(this)], + ]; + if (documentElement !== null) { + entries.push([documentElement, 'mousedown', this._onSearchClickMouseDown.bind(this), capture]); + if (this._touchInputEnabled) { + entries.push([documentElement, 'touchstart', this._onSearchClickTouchStart.bind(this), {passive: true, capture}]); + } + } + return entries; + } + /** * @returns {import('event-listener-collection').AddEventListenerArgs} */ diff --git a/types/ext/text-scanner.d.ts b/types/ext/text-scanner.d.ts index e52e74304..3df2aeea1 100644 --- a/types/ext/text-scanner.d.ts +++ b/types/ext/text-scanner.d.ts @@ -154,6 +154,8 @@ export type ConstructorDetails = { ignorePoint?: ((x: number, y: number) => Promise) | null; searchTerms?: boolean; searchKanji?: boolean; + searchOnClick?: boolean; + searchOnClickOnly?: boolean; textSourceGenerator: TextSourceGenerator; };