diff options
author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2020-09-08 19:40:15 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-08 19:40:15 -0400 |
commit | b687870a55eae43a71ea3adc41be0ab341a8721f (patch) | |
tree | 55b5046b6c2f9513edf09307c05cc50d3468ae67 /ext/fg | |
parent | ab4dbacc4c36d6373e22f758eefdb7514dc7cdb9 (diff) |
Delay hide option (#774)
* Add hideDelay option
* Add _clearSelection
* Use hideDelay
* Prevent repeated delayed selection clears
* Fix popup hide timer being cleared when the cursor is moved into the frame
Diffstat (limited to 'ext/fg')
-rw-r--r-- | ext/fg/js/frontend.js | 64 | ||||
-rw-r--r-- | ext/fg/js/popup.js | 10 |
2 files changed, 65 insertions, 9 deletions
diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 9177f985..e92feaf9 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -61,7 +61,10 @@ class Frontend { this._popupFactory = popupFactory; this._allowRootFramePopupProxy = allowRootFramePopupProxy; this._popupCache = new Map(); + this._popupEventListeners = new EventListenerCollection(); this._updatePopupToken = null; + this._clearSelectionTimer = null; + this._isPointerOverPopup = false; this._runtimeMessageHandlers = new Map([ ['requestFrontendReadyBroadcast', {async: false, handler: this._onMessageRequestFrontendReadyBroadcast.bind(this)}] @@ -175,7 +178,7 @@ class Frontend { } _onApiClosePopup() { - this._textScanner.clearSelection(false); + this._clearSelection(false); } _onApiCopySelection() { @@ -232,9 +235,11 @@ class Frontend { } _onClearSelection({passive}) { + this._stopClearSelectionDelayed(); if (this._popup !== null) { this._popup.hide(!passive); this._popup.clearAutoPlayTimer(); + this._isPointerOverPopup = false; } this._updatePendingOptions(); } @@ -249,24 +254,61 @@ class Frontend { await this.updateOptions(); } - _onSearched({textScanner, type, definitions, sentence, input: {cause}, textSource, optionsContext, error}) { + _onSearched({type, definitions, sentence, input: {cause}, textSource, optionsContext, error}) { + const scanningOptions = this._options.scanning; + if (error !== null) { if (yomichan.isExtensionUnloaded) { - if (textSource !== null && this._options.scanning.modifier !== 'none') { + if (textSource !== null && scanningOptions.modifier !== 'none') { this._showExtensionUnloaded(textSource); } } else { yomichan.logError(error); } + } if (type !== null) { + this._stopClearSelectionDelayed(); + const focus = (cause === 'mouse'); + this._showContent(textSource, focus, definitions, type, sentence, optionsContext); } else { - if (type !== null) { - const focus = (cause === 'mouse'); - this._showContent(textSource, focus, definitions, type, sentence, optionsContext); + if (scanningOptions.autoHideResults) { + this._clearSelectionDelayed(scanningOptions.hideDelay, false); } } + } + + _onPopupFramePointerOver() { + this._isPointerOverPopup = true; + this._stopClearSelectionDelayed(); + } + + _onPopupFramePointerOut() { + this._isPointerOverPopup = false; + } + + _clearSelection(passive) { + this._stopClearSelectionDelayed(); + this._textScanner.clearSelection(passive); + } + + _clearSelectionDelayed(delay, restart, passive) { + if (!this._textScanner.hasSelection()) { return; } + if (delay > 0) { + if (this._clearSelectionTimer !== null && !restart) { return; } // Already running + this._stopClearSelectionDelayed(); + this._clearSelectionTimer = setTimeout(() => { + this._clearSelectionTimer = null; + if (this._isPointerOverPopup) { return; } + this._clearSelection(passive); + }, delay); + } else { + this._clearSelection(passive); + } + } - if (type === null && this._options.scanning.autoHideResults) { - textScanner.clearSelection(false); + _stopClearSelectionDelayed() { + if (this._clearSelectionTimer !== null) { + clearTimeout(this._clearSelectionTimer); + this._clearSelectionTimer = null; } } @@ -354,8 +396,12 @@ class Frontend { this.setDisabledOverride(!this._options.scanning.enableOnSearchPage); } - this._textScanner.clearSelection(true); + this._clearSelection(true); + this._popupEventListeners.removeAllEventListeners(); this._popup = popup; + this._popupEventListeners.on(popup, 'framePointerOver', this._onPopupFramePointerOver.bind(this)); + this._popupEventListeners.on(popup, 'framePointerOut', this._onPopupFramePointerOut.bind(this)); + this._isPointerOverPopup = false; } async _getDefaultPopup() { diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index f644ee98..ee3bf646 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -95,6 +95,8 @@ class Popup extends EventDispatcher { // Public functions prepare() { + this._frame.addEventListener('mouseover', this._onFrameMouseOver.bind(this)); + this._frame.addEventListener('mouseout', this._onFrameMouseOut.bind(this)); this._frame.addEventListener('mousedown', (e) => e.stopPropagation()); this._frame.addEventListener('scroll', (e) => e.stopPropagation()); this._frame.addEventListener('load', this._onFrameLoad.bind(this)); @@ -207,6 +209,14 @@ class Popup extends EventDispatcher { // Private functions + _onFrameMouseOver() { + this.trigger('framePointerOver', {}); + } + + _onFrameMouseOut() { + this.trigger('framePointerOut', {}); + } + _inject() { let injectPromise = this._injectPromise; if (injectPromise === null) { |