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
This commit is contained in:
parent
ab4dbacc4c
commit
b687870a55
@ -324,6 +324,7 @@
|
|||||||
"alphanumeric",
|
"alphanumeric",
|
||||||
"autoHideResults",
|
"autoHideResults",
|
||||||
"delay",
|
"delay",
|
||||||
|
"hideDelay",
|
||||||
"length",
|
"length",
|
||||||
"modifier",
|
"modifier",
|
||||||
"deepDomScan",
|
"deepDomScan",
|
||||||
@ -360,6 +361,11 @@
|
|||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"default": 20
|
"default": 20
|
||||||
},
|
},
|
||||||
|
"hideDelay": {
|
||||||
|
"type": "number",
|
||||||
|
"minimum": 0,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
"length": {
|
"length": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
|
@ -473,6 +473,7 @@ class OptionsUtil {
|
|||||||
// Options conditions converted to string representations.
|
// Options conditions converted to string representations.
|
||||||
// Added usePopupWindow.
|
// Added usePopupWindow.
|
||||||
// Updated handlebars templates to include "clipboard-image" definition.
|
// Updated handlebars templates to include "clipboard-image" definition.
|
||||||
|
// Added hideDelay.
|
||||||
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) {
|
||||||
@ -487,6 +488,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;
|
||||||
}
|
}
|
||||||
await this._addFieldTemplatesToOptions(options, '/bg/data/anki-field-templates-upgrade-v4.handlebars');
|
await this._addFieldTemplatesToOptions(options, '/bg/data/anki-field-templates-upgrade-v4.handlebars');
|
||||||
return options;
|
return options;
|
||||||
|
@ -415,8 +415,16 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group options-advanced">
|
<div class="form-group options-advanced">
|
||||||
<label for="scan-delay">Scan delay <span class="label-light">(in milliseconds)</span></label>
|
<div class="row">
|
||||||
<input type="number" min="0" id="scan-delay" class="form-control" data-setting="scanning.delay">
|
<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>
|
||||||
|
|
||||||
<div class="form-group options-advanced">
|
<div class="form-group options-advanced">
|
||||||
|
@ -61,7 +61,10 @@ class Frontend {
|
|||||||
this._popupFactory = popupFactory;
|
this._popupFactory = popupFactory;
|
||||||
this._allowRootFramePopupProxy = allowRootFramePopupProxy;
|
this._allowRootFramePopupProxy = allowRootFramePopupProxy;
|
||||||
this._popupCache = new Map();
|
this._popupCache = new Map();
|
||||||
|
this._popupEventListeners = new EventListenerCollection();
|
||||||
this._updatePopupToken = null;
|
this._updatePopupToken = null;
|
||||||
|
this._clearSelectionTimer = null;
|
||||||
|
this._isPointerOverPopup = false;
|
||||||
|
|
||||||
this._runtimeMessageHandlers = new Map([
|
this._runtimeMessageHandlers = new Map([
|
||||||
['requestFrontendReadyBroadcast', {async: false, handler: this._onMessageRequestFrontendReadyBroadcast.bind(this)}]
|
['requestFrontendReadyBroadcast', {async: false, handler: this._onMessageRequestFrontendReadyBroadcast.bind(this)}]
|
||||||
@ -175,7 +178,7 @@ class Frontend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onApiClosePopup() {
|
_onApiClosePopup() {
|
||||||
this._textScanner.clearSelection(false);
|
this._clearSelection(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onApiCopySelection() {
|
_onApiCopySelection() {
|
||||||
@ -232,9 +235,11 @@ class Frontend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onClearSelection({passive}) {
|
_onClearSelection({passive}) {
|
||||||
|
this._stopClearSelectionDelayed();
|
||||||
if (this._popup !== null) {
|
if (this._popup !== null) {
|
||||||
this._popup.hide(!passive);
|
this._popup.hide(!passive);
|
||||||
this._popup.clearAutoPlayTimer();
|
this._popup.clearAutoPlayTimer();
|
||||||
|
this._isPointerOverPopup = false;
|
||||||
}
|
}
|
||||||
this._updatePendingOptions();
|
this._updatePendingOptions();
|
||||||
}
|
}
|
||||||
@ -249,24 +254,61 @@ class Frontend {
|
|||||||
await this.updateOptions();
|
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 (error !== null) {
|
||||||
if (yomichan.isExtensionUnloaded) {
|
if (yomichan.isExtensionUnloaded) {
|
||||||
if (textSource !== null && this._options.scanning.modifier !== 'none') {
|
if (textSource !== null && scanningOptions.modifier !== 'none') {
|
||||||
this._showExtensionUnloaded(textSource);
|
this._showExtensionUnloaded(textSource);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
yomichan.logError(error);
|
yomichan.logError(error);
|
||||||
}
|
}
|
||||||
|
} if (type !== null) {
|
||||||
|
this._stopClearSelectionDelayed();
|
||||||
|
const focus = (cause === 'mouse');
|
||||||
|
this._showContent(textSource, focus, definitions, type, sentence, optionsContext);
|
||||||
} else {
|
} else {
|
||||||
if (type !== null) {
|
if (scanningOptions.autoHideResults) {
|
||||||
const focus = (cause === 'mouse');
|
this._clearSelectionDelayed(scanningOptions.hideDelay, false);
|
||||||
this._showContent(textSource, focus, definitions, type, sentence, optionsContext);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (type === null && this._options.scanning.autoHideResults) {
|
_onPopupFramePointerOver() {
|
||||||
textScanner.clearSelection(false);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_stopClearSelectionDelayed() {
|
||||||
|
if (this._clearSelectionTimer !== null) {
|
||||||
|
clearTimeout(this._clearSelectionTimer);
|
||||||
|
this._clearSelectionTimer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,8 +396,12 @@ class Frontend {
|
|||||||
this.setDisabledOverride(!this._options.scanning.enableOnSearchPage);
|
this.setDisabledOverride(!this._options.scanning.enableOnSearchPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._textScanner.clearSelection(true);
|
this._clearSelection(true);
|
||||||
|
this._popupEventListeners.removeAllEventListeners();
|
||||||
this._popup = popup;
|
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() {
|
async _getDefaultPopup() {
|
||||||
|
@ -95,6 +95,8 @@ class Popup extends EventDispatcher {
|
|||||||
// Public functions
|
// Public functions
|
||||||
|
|
||||||
prepare() {
|
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('mousedown', (e) => e.stopPropagation());
|
||||||
this._frame.addEventListener('scroll', (e) => e.stopPropagation());
|
this._frame.addEventListener('scroll', (e) => e.stopPropagation());
|
||||||
this._frame.addEventListener('load', this._onFrameLoad.bind(this));
|
this._frame.addEventListener('load', this._onFrameLoad.bind(this));
|
||||||
@ -207,6 +209,14 @@ class Popup extends EventDispatcher {
|
|||||||
|
|
||||||
// Private functions
|
// Private functions
|
||||||
|
|
||||||
|
_onFrameMouseOver() {
|
||||||
|
this.trigger('framePointerOver', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
_onFrameMouseOut() {
|
||||||
|
this.trigger('framePointerOut', {});
|
||||||
|
}
|
||||||
|
|
||||||
_inject() {
|
_inject() {
|
||||||
let injectPromise = this._injectPromise;
|
let injectPromise = this._injectPromise;
|
||||||
if (injectPromise === null) {
|
if (injectPromise === null) {
|
||||||
|
@ -144,6 +144,10 @@ class TextScanner extends EventDispatcher {
|
|||||||
return clonedTextSource.text();
|
return clonedTextSource.text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasSelection() {
|
||||||
|
return (this._textSourceCurrent !== null);
|
||||||
|
}
|
||||||
|
|
||||||
clearSelection(passive) {
|
clearSelection(passive) {
|
||||||
if (!this._canClearSelection) { return; }
|
if (!this._canClearSelection) { return; }
|
||||||
if (this._textSourceCurrent !== null) {
|
if (this._textSourceCurrent !== null) {
|
||||||
|
Loading…
Reference in New Issue
Block a user