From cb566f015aa280499da94126a3f6336c4d8ce0df Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Wed, 14 Oct 2020 19:37:46 -0400 Subject: Add simple scan input UI (#921) * Add simple scan input UI * Create helper function * Add controller for old scanning input UI * Add refresh functions * Add abstraction function * Fix incomplete middle mouse support detection * Make scanning inputs update eachother * Fix global declaration order --- ext/bg/css/settings.css | 1 + ext/bg/js/settings/main.js | 4 + ext/bg/js/settings/scan-inputs-controller.js | 58 ++++-- .../js/settings/scan-inputs-simple-controller.js | 221 +++++++++++++++++++++ ext/bg/settings.html | 12 +- 5 files changed, 276 insertions(+), 20 deletions(-) create mode 100644 ext/bg/js/settings/scan-inputs-simple-controller.js diff --git a/ext/bg/css/settings.css b/ext/bg/css/settings.css index 4a49b98d..8d6a3911 100644 --- a/ext/bg/css/settings.css +++ b/ext/bg/css/settings.css @@ -23,6 +23,7 @@ html:root:not([data-options-anki-enable=true]) #anki-general, html:root:not([data-options-general-debug-info=true]) .debug, html:root:not([data-options-general-show-advanced=true]) .options-advanced, +html:root[data-options-general-show-advanced=true] .options-non-advanced, html:root:not([data-options-general-result-output-mode=merge]) #dict-main-group { display: none; } diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js index 37c6375d..382487eb 100644 --- a/ext/bg/js/settings/main.js +++ b/ext/bg/js/settings/main.js @@ -27,6 +27,7 @@ * PopupPreviewController * ProfileController * ScanInputsController + * ScanInputsSimpleController * SettingsController * StorageController * api @@ -96,6 +97,9 @@ async function setupEnvironmentInfo() { const scanInputsController = new ScanInputsController(settingsController); scanInputsController.prepare(); + const simpleScanningInputController = new ScanInputsSimpleController(settingsController); + simpleScanningInputController.prepare(); + yomichan.ready(); } catch (e) { yomichan.logError(e); diff --git a/ext/bg/js/settings/scan-inputs-controller.js b/ext/bg/js/settings/scan-inputs-controller.js index ec2758cb..fe994aed 100644 --- a/ext/bg/js/settings/scan-inputs-controller.js +++ b/ext/bg/js/settings/scan-inputs-controller.js @@ -37,10 +37,10 @@ class ScanInputsController { this._addButton = document.querySelector('#scan-input-add'); this._addButton.addEventListener('click', this._onAddButtonClick.bind(this), false); + this._settingsController.on('scanInputsChanged', this._onScanInputsChanged.bind(this)); this._settingsController.on('optionsChanged', this._onOptionsChanged.bind(this)); - const options = await this._settingsController.getOptions(); - this._onOptionsChanged({options}); + this.refresh(); } removeInput(index) { @@ -51,13 +51,14 @@ class ScanInputsController { for (let i = index, ii = this._entries.length; i < ii; ++i) { this._entries[i].index = i; } - this._settingsController.modifyProfileSettings([{ + this._modifyProfileSettings([{ action: 'splice', path: 'scanning.inputs', start: index, deleteCount: 1, items: [] }]); + return true; } setProperty(index, property, value) { @@ -65,8 +66,18 @@ class ScanInputsController { this._settingsController.setProfileSetting(path, value); } + async refresh() { + const options = await this._settingsController.getOptions(); + this._onOptionsChanged({options}); + } + // Private + _onScanInputsChanged({source}) { + if (source === this) { return; } + this.refresh(); + } + _onOptionsChanged({options}) { const {inputs} = options.scanning; @@ -88,26 +99,12 @@ class ScanInputsController { const include = ''; const exclude = ''; this._addOption(index, include, exclude); - this._settingsController.modifyProfileSettings([{ + this._modifyProfileSettings([{ action: 'splice', path: 'scanning.inputs', start: index, deleteCount: 0, - items: [{ - include, - exclude, - types: {mouse: true, touch: false, pen: false}, - options: { - showAdvanced: false, - searchTerms: true, - searchKanji: true, - scanOnTouchMove: true, - scanOnPenHover: true, - scanOnPenPress: true, - scanOnPenRelease: false, - preventTouchScrolling: true - } - }] + items: [ScanInputsController.createDefaultMouseInput(include, exclude)] }]); } @@ -116,6 +113,29 @@ class ScanInputsController { this._entries.push(field); field.prepare(this._container, include, exclude); } + + async _modifyProfileSettings(targets) { + await this._settingsController.modifyProfileSettings(targets); + this._settingsController.trigger('scanInputsChanged', {source: this}); + } + + static createDefaultMouseInput(include, exclude) { + return { + include, + exclude, + types: {mouse: true, touch: false, pen: false}, + options: { + showAdvanced: false, + searchTerms: true, + searchKanji: true, + scanOnTouchMove: true, + scanOnPenHover: true, + scanOnPenPress: true, + scanOnPenRelease: false, + preventTouchScrolling: true + } + }; + } } class ScanInputField { diff --git a/ext/bg/js/settings/scan-inputs-simple-controller.js b/ext/bg/js/settings/scan-inputs-simple-controller.js new file mode 100644 index 00000000..636fd102 --- /dev/null +++ b/ext/bg/js/settings/scan-inputs-simple-controller.js @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2020 Yomichan Authors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/* global + * DocumentUtil + * ScanInputsController + * api + */ + +class ScanInputsSimpleController { + constructor(settingsController) { + this._settingsController = settingsController; + this._middleMouseButtonScan = null; + this._mainScanModifierKeyInput = null; + } + + async prepare() { + this._middleMouseButtonScan = document.querySelector('#middle-mouse-button-scan'); + this._mainScanModifierKeyInput = document.querySelector('#main-scan-modifier-key'); + + const {platform: {os}} = await api.getEnvironmentInfo(); + this._populateSelect(this._mainScanModifierKeyInput, os); + + const options = await this._settingsController.getOptions(); + + this._middleMouseButtonScan.addEventListener('change', this.onMiddleMouseButtonScanChange.bind(this), false); + this._mainScanModifierKeyInput.addEventListener('change', this._onMainScanModifierKeyInputChange.bind(this), false); + + this._settingsController.on('scanInputsChanged', this._onScanInputsChanged.bind(this)); + this._settingsController.on('optionsChanged', this._onOptionsChanged.bind(this)); + this._onOptionsChanged({options}); + } + + async refresh() { + const options = await this._settingsController.getOptions(); + this._onOptionsChanged({options}); + } + + // Private + + _onScanInputsChanged({source}) { + if (source === this) { return; } + this.refresh(); + } + + _onOptionsChanged({options}) { + const {scanning: {inputs}} = options; + const middleMouseSupportedIndex = this._getIndexOfMiddleMouseButtonScanInput(inputs); + const mainScanInputIndex = this._getIndexOfMainScanInput(inputs); + + let middleMouseSupported = false; + if (middleMouseSupportedIndex >= 0) { + const includeValues = this._splitValue(inputs[middleMouseSupportedIndex].include); + if (includeValues.includes('mouse2')) { + middleMouseSupported = true; + } + } + + let mainScanInput = 'none'; + if (mainScanInputIndex >= 0) { + const includeValues = this._splitValue(inputs[mainScanInputIndex].include); + if (includeValues.length > 0) { + mainScanInput = includeValues[0]; + } + } + + this._middleMouseButtonScan.checked = middleMouseSupported; + this._mainScanModifierKeyInput.value = mainScanInput; + } + + onMiddleMouseButtonScanChange(e) { + const middleMouseSupported = e.currentTarget.checked; + this._setMiddleMouseSuppported(middleMouseSupported); + } + + _onMainScanModifierKeyInputChange(e) { + const mainScanKey = e.currentTarget.value; + const mainScanInputs = (mainScanKey === 'none' ? [] : [mainScanKey]); + this._setMainScanInputs(mainScanInputs); + } + + _populateSelect(select, os) { + const modifierKeys = [ + {value: 'none', name: 'None'}, + ...DocumentUtil.getModifierKeys(os).map(([value, name]) => ({value, name})) + ]; + + const fragment = document.createDocumentFragment(); + for (const {value, name} of modifierKeys) { + const option = document.createElement('option'); + option.value = value; + option.textContent = name; + fragment.appendChild(option); + } + select.textContent = ''; + select.appendChild(fragment); + } + + _splitValue(value) { + return value.split(/[,;\s]+/).map((v) => v.trim().toLowerCase()).filter((v) => v.length > 0); + } + + async _setMiddleMouseSuppported(value) { + // Find target index + const options = await this._settingsController.getOptions(); + const {scanning: {inputs}} = options; + const index = this._getIndexOfMiddleMouseButtonScanInput(inputs); + + if (value) { + // Add new + if (index >= 0) { return; } + let insertionPosition = this._getIndexOfMainScanInput(inputs); + insertionPosition = (insertionPosition >= 0 ? insertionPosition + 1 : inputs.length); + const input = ScanInputsController.createDefaultMouseInput('mouse2', ''); + await this._modifyProfileSettings([{ + action: 'splice', + path: 'scanning.inputs', + start: insertionPosition, + deleteCount: 0, + items: [input] + }]); + } else { + // Modify existing + if (index < 0) { return; } + await this._modifyProfileSettings([{ + action: 'splice', + path: 'scanning.inputs', + start: index, + deleteCount: 1, + items: [] + }]); + } + } + + async _setMainScanInputs(value) { + value = value.join(', '); + + // Find target index + const options = await this._settingsController.getOptions(); + const {scanning: {inputs}} = options; + const index = this._getIndexOfMainScanInput(inputs); + + if (index < 0) { + // Add new + const input = ScanInputsController.createDefaultMouseInput(value, 'mouse0'); + await this._modifyProfileSettings([{ + action: 'splice', + path: 'scanning.inputs', + start: inputs.length, + deleteCount: 0, + items: [input] + }]); + } else { + // Modify existing + await this._modifyProfileSettings([{ + action: 'set', + path: `scanning.inputs[${index}].include`, + value + }]); + } + } + + async _modifyProfileSettings(targets) { + await this._settingsController.modifyProfileSettings(targets); + this._settingsController.trigger('scanInputsChanged', {source: this}); + } + + _getIndexOfMainScanInput(inputs) { + for (let i = 0, ii = inputs.length; i < ii; ++i) { + const {include, exclude, types: {mouse}} = inputs[i]; + if (!mouse) { continue; } + const includeValues = this._splitValue(include); + const excludeValues = this._splitValue(exclude); + if ( + ( + includeValues.length === 0 || + (includeValues.length === 1 && !this._isMouseInput(includeValues[0])) + ) && + excludeValues.length === 1 && + excludeValues[0] === 'mouse0' + ) { + return i; + } + } + return -1; + } + + _getIndexOfMiddleMouseButtonScanInput(inputs) { + for (let i = 0, ii = inputs.length; i < ii; ++i) { + const {include, exclude, types: {mouse}} = inputs[i]; + if (!mouse) { continue; } + const includeValues = this._splitValue(include); + const excludeValues = this._splitValue(exclude); + if ( + (includeValues.length === 0 || (includeValues.length === 1 && includeValues.includes('mouse2'))) && + excludeValues.length === 0 + ) { + return i; + } + } + return -1; + } + + _isMouseInput(input) { + return /^mouse\d+$/.test(input); + } +} \ No newline at end of file diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 86535af4..4b7bd086 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -386,6 +386,10 @@

Scanning Options

+
+ +
+
@@ -442,7 +446,12 @@
-
+
+ + +
+ +
@@ -1242,6 +1251,7 @@ + -- cgit v1.2.3