diff options
Diffstat (limited to 'ext')
| -rw-r--r-- | ext/bg/data/options-schema.json | 6 | ||||
| -rw-r--r-- | ext/bg/js/options.js | 2 | ||||
| -rw-r--r-- | ext/bg/settings.html | 12 | ||||
| -rw-r--r-- | ext/fg/js/frontend.js | 64 | ||||
| -rw-r--r-- | ext/fg/js/popup.js | 10 | ||||
| -rw-r--r-- | ext/mixed/js/text-scanner.js | 4 | 
6 files changed, 87 insertions, 11 deletions
| diff --git a/ext/bg/data/options-schema.json b/ext/bg/data/options-schema.json index e2dd0573..52d96db9 100644 --- a/ext/bg/data/options-schema.json +++ b/ext/bg/data/options-schema.json @@ -324,6 +324,7 @@                                      "alphanumeric",                                      "autoHideResults",                                      "delay", +                                    "hideDelay",                                      "length",                                      "modifier",                                      "deepDomScan", @@ -360,6 +361,11 @@                                          "minimum": 0,                                          "default": 20                                      }, +                                    "hideDelay": { +                                        "type": "number", +                                        "minimum": 0, +                                        "default": 0 +                                    },                                      "length": {                                          "type": "integer",                                          "minimum": 1, diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index 9dc0c166..398fb95c 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -473,6 +473,7 @@ class OptionsUtil {          //  Options conditions converted to string representations.          //  Added usePopupWindow.          //  Updated handlebars templates to include "clipboard-image" definition. +        //  Added hideDelay.          for (const {conditionGroups} of options.profiles) {              for (const {conditions} of conditionGroups) {                  for (const condition of conditions) { @@ -487,6 +488,7 @@ class OptionsUtil {          }          for (const {options: profileOptions} of options.profiles) {              profileOptions.general.usePopupWindow = false; +            profileOptions.scanning.hideDelay = 0;          }          await this._addFieldTemplatesToOptions(options, '/bg/data/anki-field-templates-upgrade-v4.handlebars');          return options; diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 0ad5b79b..3fa14f49 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -415,8 +415,16 @@                  </div>                  <div class="form-group options-advanced"> -                    <label for="scan-delay">Scan delay <span class="label-light">(in milliseconds)</span></label> -                    <input type="number" min="0" id="scan-delay" class="form-control" data-setting="scanning.delay"> +                    <div class="row"> +                        <div class="col-xs-6"> +                            <label for="scan-delay">Scan delay <span class="label-light">(in milliseconds)</span></label> +                            <input type="number" min="0" id="scan-delay" class="form-control" data-setting="scanning.delay"> +                        </div> +                        <div class="col-xs-6"> +                            <label for="scan-close-delay">Auto-hide delay <span class="label-light">(in milliseconds)</span></label> +                            <input type="number" min="0" id="scan-close-delay" class="form-control" data-setting="scanning.hideDelay"> +                        </div> +                    </div>                  </div>                  <div class="form-group options-advanced"> 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) { diff --git a/ext/mixed/js/text-scanner.js b/ext/mixed/js/text-scanner.js index 2410f2b7..f3e99577 100644 --- a/ext/mixed/js/text-scanner.js +++ b/ext/mixed/js/text-scanner.js @@ -144,6 +144,10 @@ class TextScanner extends EventDispatcher {          return clonedTextSource.text();      } +    hasSelection() { +        return (this._textSourceCurrent !== null); +    } +      clearSelection(passive) {          if (!this._canClearSelection) { return; }          if (this._textSourceCurrent !== null) { |