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
This commit is contained in:
toasted-nutbread 2020-10-14 19:37:46 -04:00 committed by GitHub
parent 429e3a5b74
commit 51904761ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 276 additions and 20 deletions

View File

@ -23,6 +23,7 @@
html:root:not([data-options-anki-enable=true]) #anki-general, 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-debug-info=true]) .debug,
html:root:not([data-options-general-show-advanced=true]) .options-advanced, 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]) #dictionary-main-group { html:root:not([data-options-general-result-output-mode=merge]) #dictionary-main-group {
display: none; display: none;
} }

View File

@ -28,6 +28,7 @@
* PopupPreviewController * PopupPreviewController
* ProfileController * ProfileController
* ScanInputsController * ScanInputsController
* ScanInputsSimpleController
* SettingsController * SettingsController
* StorageController * StorageController
* api * api
@ -100,6 +101,9 @@ async function setupEnvironmentInfo() {
const scanInputsController = new ScanInputsController(settingsController); const scanInputsController = new ScanInputsController(settingsController);
scanInputsController.prepare(); scanInputsController.prepare();
const simpleScanningInputController = new ScanInputsSimpleController(settingsController);
simpleScanningInputController.prepare();
yomichan.ready(); yomichan.ready();
} catch (e) { } catch (e) {
yomichan.logError(e); yomichan.logError(e);

View File

@ -37,10 +37,10 @@ class ScanInputsController {
this._addButton = document.querySelector('#scan-input-add'); this._addButton = document.querySelector('#scan-input-add');
this._addButton.addEventListener('click', this._onAddButtonClick.bind(this), false); 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)); this._settingsController.on('optionsChanged', this._onOptionsChanged.bind(this));
const options = await this._settingsController.getOptions(); this.refresh();
this._onOptionsChanged({options});
} }
removeInput(index) { removeInput(index) {
@ -51,13 +51,14 @@ class ScanInputsController {
for (let i = index, ii = this._entries.length; i < ii; ++i) { for (let i = index, ii = this._entries.length; i < ii; ++i) {
this._entries[i].index = i; this._entries[i].index = i;
} }
this._settingsController.modifyProfileSettings([{ this._modifyProfileSettings([{
action: 'splice', action: 'splice',
path: 'scanning.inputs', path: 'scanning.inputs',
start: index, start: index,
deleteCount: 1, deleteCount: 1,
items: [] items: []
}]); }]);
return true;
} }
setProperty(index, property, value) { setProperty(index, property, value) {
@ -69,8 +70,18 @@ class ScanInputsController {
return this._settingsController.instantiateTemplate(name); return this._settingsController.instantiateTemplate(name);
} }
async refresh() {
const options = await this._settingsController.getOptions();
this._onOptionsChanged({options});
}
// Private // Private
_onScanInputsChanged({source}) {
if (source === this) { return; }
this.refresh();
}
_onOptionsChanged({options}) { _onOptionsChanged({options}) {
const {inputs} = options.scanning; const {inputs} = options.scanning;
@ -92,12 +103,28 @@ class ScanInputsController {
const include = ''; const include = '';
const exclude = ''; const exclude = '';
this._addOption(index, include, exclude); this._addOption(index, include, exclude);
this._settingsController.modifyProfileSettings([{ this._modifyProfileSettings([{
action: 'splice', action: 'splice',
path: 'scanning.inputs', path: 'scanning.inputs',
start: index, start: index,
deleteCount: 0, deleteCount: 0,
items: [{ items: [ScanInputsController.createDefaultMouseInput(include, exclude)]
}]);
}
_addOption(index, include, exclude) {
const field = new ScanInputField(this, index, this._os);
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, include,
exclude, exclude,
types: {mouse: true, touch: false, pen: false}, types: {mouse: true, touch: false, pen: false},
@ -111,14 +138,7 @@ class ScanInputsController {
scanOnPenRelease: false, scanOnPenRelease: false,
preventTouchScrolling: true preventTouchScrolling: true
} }
}] };
}]);
}
_addOption(index, include, exclude) {
const field = new ScanInputField(this, index, this._os);
this._entries.push(field);
field.prepare(this._container, include, exclude);
} }
} }

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
/* 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);
}
}

View File

@ -387,6 +387,10 @@
<div> <div>
<h3>Scanning Options</h3> <h3>Scanning Options</h3>
<div class="checkbox options-non-advanced">
<label><input type="checkbox" id="middle-mouse-button-scan"> Middle mouse button scans</label>
</div>
<div class="checkbox"> <div class="checkbox">
<label><input type="checkbox" id="select-matched-text" data-setting="scanning.selectText"> Select matched text</label> <label><input type="checkbox" id="select-matched-text" data-setting="scanning.selectText"> Select matched text</label>
</div> </div>
@ -443,7 +447,12 @@
<input type="number" min="1" step="1" id="scan-length" class="form-control" data-setting="scanning.length"> <input type="number" min="1" step="1" id="scan-length" class="form-control" data-setting="scanning.length">
</div> </div>
<div class="form-group"> <div class="form-group options-non-advanced">
<label for="main-scan-modifier-key">Scan modifier key</label>
<select class="form-control" id="main-scan-modifier-key"></select>
</div>
<div class="form-group options-advanced">
<label>Scan inputs</label> <label>Scan inputs</label>
<div class="scan-input-list" id="scan-input-list"></div> <div class="scan-input-list" id="scan-input-list"></div>
<button class="btn btn-default" id="scan-input-add" title="Add scan input"><span class="glyphicon glyphicon-plus"></span></button> <button class="btn btn-default" id="scan-input-add" title="Add scan input"><span class="glyphicon glyphicon-plus"></span></button>
@ -1255,6 +1264,7 @@
<script src="/bg/js/settings/popup-preview-controller.js"></script> <script src="/bg/js/settings/popup-preview-controller.js"></script>
<script src="/bg/js/settings/profile-controller.js"></script> <script src="/bg/js/settings/profile-controller.js"></script>
<script src="/bg/js/settings/scan-inputs-controller.js"></script> <script src="/bg/js/settings/scan-inputs-controller.js"></script>
<script src="/bg/js/settings/scan-inputs-simple-controller.js"></script>
<script src="/bg/js/settings/settings-controller.js"></script> <script src="/bg/js/settings/settings-controller.js"></script>
<script src="/bg/js/settings/storage-controller.js"></script> <script src="/bg/js/settings/storage-controller.js"></script>