diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js index c7e8c9d8..e1b531d4 100644 --- a/ext/js/app/frontend.js +++ b/ext/js/app/frontend.js @@ -683,7 +683,7 @@ class Frontend { const range = this._getFirstNonEmptySelectionRange(); if (range === null) { return false; } const source = new TextSourceRange(range, range.toString(), null, null); - await this._textScanner.search(source, {focus: true}); + await this._textScanner.search(source, {focus: true, restoreSelection: true}); return true; } diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js index b571435b..a49627f8 100644 --- a/ext/js/language/text-scanner.js +++ b/ext/js/language/text-scanner.js @@ -52,6 +52,7 @@ class TextScanner extends EventDispatcher { this._textSourceCurrent = null; this._textSourceCurrentSelected = false; this._pendingLookup = false; + this._selectionRestoreInfo = null; this._deepContentScan = false; this._selectText = false; @@ -258,6 +259,10 @@ class TextScanner extends EventDispatcher { if (this._textSourceCurrent !== null) { if (this._textSourceCurrentSelected) { this._textSourceCurrent.deselect(); + if (this._selectionRestoreInfo !== null) { + this._restoreSelection(this._selectionRestoreInfo); + this._selectionRestoreInfo = null; + } } this._textSourceCurrent = null; this._textSourceCurrentSelected = false; @@ -313,6 +318,13 @@ class TextScanner extends EventDispatcher { let detail = null; try { + const inputInfoDetail = inputInfo.detail; + const selectionRestoreInfo = ( + (isObject(inputInfoDetail) && inputInfoDetail.restoreSelection) ? + (this._inputInfoCurrent === null ? this._createSelectionRestoreInfo() : void 0) : + null + ); + if (this._textSourceCurrent !== null && this._textSourceCurrent.hasSameStart(textSource)) { return null; } @@ -322,17 +334,24 @@ class TextScanner extends EventDispatcher { searched = true; + let valid = false; const result = await this._findDictionaryEntries(textSource, searchTerms, searchKanji, optionsContext); if (result !== null) { ({dictionaryEntries, sentence, type} = result); - this._inputInfoCurrent = inputInfo; - this.setCurrentTextSource(textSource); + valid = true; } else if (textSource instanceof TextSourceElement && await this._hasJapanese(textSource.fullContent)) { dictionaryEntries = []; sentence = {sentence: '', offset: 0}; type = 'terms'; + valid = true; + } + + if (valid) { this._inputInfoCurrent = inputInfo; this.setCurrentTextSource(textSource); + if (typeof selectionRestoreInfo !== 'undefined') { + this._selectionRestoreInfo = selectionRestoreInfo; + } } } catch (e) { error = e; @@ -1054,4 +1073,27 @@ class TextScanner extends EventDispatcher { return false; } } + + _createSelectionRestoreInfo() { + const ranges = []; + const selection = window.getSelection(); + for (let i = 0, ii = selection.rangeCount; i < ii; ++i) { + const range = selection.getRangeAt(i); + ranges.push(range.cloneRange()); + } + return {ranges}; + } + + _restoreSelection(selectionRestoreInfo) { + const {ranges} = selectionRestoreInfo; + const selection = window.getSelection(); + selection.removeAllRanges(); + for (const range of ranges) { + try { + selection.addRange(range); + } catch (e) { + // NOP + } + } + } }