Merge pull request #157 from toasted-nutbread/touch-input
Add support for touch input
This commit is contained in:
commit
c7f8d0874a
@ -190,6 +190,7 @@ function optionsSetDefaults(options) {
|
|||||||
debugInfo: false,
|
debugInfo: false,
|
||||||
maxResults: 32,
|
maxResults: 32,
|
||||||
showAdvanced: false,
|
showAdvanced: false,
|
||||||
|
popupDisplayMode: 'default',
|
||||||
popupWidth: 400,
|
popupWidth: 400,
|
||||||
popupHeight: 250,
|
popupHeight: 250,
|
||||||
popupOffset: 10,
|
popupOffset: 10,
|
||||||
@ -201,6 +202,7 @@ function optionsSetDefaults(options) {
|
|||||||
|
|
||||||
scanning: {
|
scanning: {
|
||||||
middleMouse: true,
|
middleMouse: true,
|
||||||
|
touchInputEnabled: true,
|
||||||
selectText: true,
|
selectText: true,
|
||||||
alphanumeric: true,
|
alphanumeric: true,
|
||||||
autoHideResults: false,
|
autoHideResults: false,
|
||||||
|
@ -31,11 +31,13 @@ async function formRead() {
|
|||||||
optionsNew.general.debugInfo = $('#show-debug-info').prop('checked');
|
optionsNew.general.debugInfo = $('#show-debug-info').prop('checked');
|
||||||
optionsNew.general.showAdvanced = $('#show-advanced-options').prop('checked');
|
optionsNew.general.showAdvanced = $('#show-advanced-options').prop('checked');
|
||||||
optionsNew.general.maxResults = parseInt($('#max-displayed-results').val(), 10);
|
optionsNew.general.maxResults = parseInt($('#max-displayed-results').val(), 10);
|
||||||
|
optionsNew.general.popupDisplayMode = $('#popup-display-mode').val();
|
||||||
optionsNew.general.popupWidth = parseInt($('#popup-width').val(), 10);
|
optionsNew.general.popupWidth = parseInt($('#popup-width').val(), 10);
|
||||||
optionsNew.general.popupHeight = parseInt($('#popup-height').val(), 10);
|
optionsNew.general.popupHeight = parseInt($('#popup-height').val(), 10);
|
||||||
optionsNew.general.popupOffset = parseInt($('#popup-offset').val(), 10);
|
optionsNew.general.popupOffset = parseInt($('#popup-offset').val(), 10);
|
||||||
|
|
||||||
optionsNew.scanning.middleMouse = $('#middle-mouse-button-scan').prop('checked');
|
optionsNew.scanning.middleMouse = $('#middle-mouse-button-scan').prop('checked');
|
||||||
|
optionsNew.scanning.touchInputEnabled = $('#touch-input-enabled').prop('checked');
|
||||||
optionsNew.scanning.selectText = $('#select-matched-text').prop('checked');
|
optionsNew.scanning.selectText = $('#select-matched-text').prop('checked');
|
||||||
optionsNew.scanning.alphanumeric = $('#search-alphanumeric').prop('checked');
|
optionsNew.scanning.alphanumeric = $('#search-alphanumeric').prop('checked');
|
||||||
optionsNew.scanning.autoHideResults = $('#auto-hide-results').prop('checked');
|
optionsNew.scanning.autoHideResults = $('#auto-hide-results').prop('checked');
|
||||||
@ -161,11 +163,13 @@ async function onReady() {
|
|||||||
$('#show-debug-info').prop('checked', options.general.debugInfo);
|
$('#show-debug-info').prop('checked', options.general.debugInfo);
|
||||||
$('#show-advanced-options').prop('checked', options.general.showAdvanced);
|
$('#show-advanced-options').prop('checked', options.general.showAdvanced);
|
||||||
$('#max-displayed-results').val(options.general.maxResults);
|
$('#max-displayed-results').val(options.general.maxResults);
|
||||||
|
$('#popup-display-mode').val(options.general.popupDisplayMode);
|
||||||
$('#popup-width').val(options.general.popupWidth);
|
$('#popup-width').val(options.general.popupWidth);
|
||||||
$('#popup-height').val(options.general.popupHeight);
|
$('#popup-height').val(options.general.popupHeight);
|
||||||
$('#popup-offset').val(options.general.popupOffset);
|
$('#popup-offset').val(options.general.popupOffset);
|
||||||
|
|
||||||
$('#middle-mouse-button-scan').prop('checked', options.scanning.middleMouse);
|
$('#middle-mouse-button-scan').prop('checked', options.scanning.middleMouse);
|
||||||
|
$('#touch-input-enabled').prop('checked', options.scanning.touchInputEnabled);
|
||||||
$('#select-matched-text').prop('checked', options.scanning.selectText);
|
$('#select-matched-text').prop('checked', options.scanning.selectText);
|
||||||
$('#search-alphanumeric').prop('checked', options.scanning.alphanumeric);
|
$('#search-alphanumeric').prop('checked', options.scanning.alphanumeric);
|
||||||
$('#auto-hide-results').prop('checked', options.scanning.autoHideResults);
|
$('#auto-hide-results').prop('checked', options.scanning.autoHideResults);
|
||||||
|
@ -74,6 +74,14 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="popup-display-mode">Popup display mode</label>
|
||||||
|
<select class="form-control" id="popup-display-mode">
|
||||||
|
<option value="default">Default</option>
|
||||||
|
<option value="full-width">Full width</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-group options-advanced">
|
<div class="form-group options-advanced">
|
||||||
<label for="audio-playback-volume">Audio playback volume (percent)</label>
|
<label for="audio-playback-volume">Audio playback volume (percent)</label>
|
||||||
<input type="number" min="0" max="100" id="audio-playback-volume" class="form-control">
|
<input type="number" min="0" max="100" id="audio-playback-volume" class="form-control">
|
||||||
@ -105,6 +113,10 @@
|
|||||||
<label><input type="checkbox" id="middle-mouse-button-scan"> Middle mouse button scans</label>
|
<label><input type="checkbox" id="middle-mouse-button-scan"> Middle mouse button scans</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="checkbox">
|
||||||
|
<label><input type="checkbox" id="touch-input-enabled"> Touch input enabled</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label><input type="checkbox" id="select-matched-text"> Select matched text</label>
|
<label><input type="checkbox" id="select-matched-text"> Select matched text</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,3 +27,25 @@ iframe#yomichan-float {
|
|||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
z-index: 2147483647;
|
z-index: 2147483647;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iframe#yomichan-float.yomichan-float-full-width {
|
||||||
|
border-left: none;
|
||||||
|
border-right: none;
|
||||||
|
left: 0 !important;
|
||||||
|
right: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
box-sizing: border-box;
|
||||||
|
resize: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe#yomichan-float.yomichan-float-full-width:not(.yomichan-float-above) {
|
||||||
|
border-bottom: none;
|
||||||
|
top: auto !important;
|
||||||
|
bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
iframe#yomichan-float.yomichan-float-full-width.yomichan-float-above {
|
||||||
|
border-top: none;
|
||||||
|
top: 0 !important;
|
||||||
|
bottom: auto !important;
|
||||||
|
}
|
||||||
|
@ -85,14 +85,24 @@ function docRangeFromPoint(point) {
|
|||||||
range.setEnd(position.offsetNode, position.offset);
|
range.setEnd(position.offsetNode, position.offset);
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const range = document.caretRangeFromPoint(point.x, point.y);
|
const range = document.caretRangeFromPoint(point.x, point.y);
|
||||||
|
if (range === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(imposter !== null) imposter.style.zIndex = -2147483646;
|
if(imposter !== null) imposter.style.zIndex = -2147483646;
|
||||||
|
|
||||||
const rect = range.getClientRects()[0];
|
const rects = range.getClientRects();
|
||||||
|
|
||||||
|
if (rects.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rect = rects[0];
|
||||||
if (point.y > rect.bottom + 2) {
|
if (point.y > rect.bottom + 2) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,14 @@ class Frontend {
|
|||||||
this.textSourceLast = null;
|
this.textSourceLast = null;
|
||||||
this.pendingLookup = false;
|
this.pendingLookup = false;
|
||||||
this.options = null;
|
this.options = null;
|
||||||
|
|
||||||
|
this.primaryTouchIdentifier = null;
|
||||||
|
this.contextMenuChecking = false;
|
||||||
|
this.contextMenuPrevent = false;
|
||||||
|
this.contextMenuPreviousRange = null;
|
||||||
|
this.mouseDownPrevent = false;
|
||||||
|
this.clickPrevent = false;
|
||||||
|
this.scrollPrevent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async prepare() {
|
async prepare() {
|
||||||
@ -39,6 +47,15 @@ class Frontend {
|
|||||||
window.addEventListener('mouseup', this.onMouseUp.bind(this));
|
window.addEventListener('mouseup', this.onMouseUp.bind(this));
|
||||||
window.addEventListener('resize', this.onResize.bind(this));
|
window.addEventListener('resize', this.onResize.bind(this));
|
||||||
|
|
||||||
|
if (this.options.scanning.touchInputEnabled) {
|
||||||
|
window.addEventListener('click', this.onClick.bind(this));
|
||||||
|
window.addEventListener('touchstart', this.onTouchStart.bind(this));
|
||||||
|
window.addEventListener('touchend', this.onTouchEnd.bind(this));
|
||||||
|
window.addEventListener('touchcancel', this.onTouchCancel.bind(this));
|
||||||
|
window.addEventListener('touchmove', this.onTouchMove.bind(this), {passive: false});
|
||||||
|
window.addEventListener('contextmenu', this.onContextMenu.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
chrome.runtime.onMessage.addListener(this.onBgMessage.bind(this));
|
chrome.runtime.onMessage.addListener(this.onBgMessage.bind(this));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.onError(e);
|
this.onError(e);
|
||||||
@ -79,7 +96,7 @@ class Frontend {
|
|||||||
|
|
||||||
const search = async () => {
|
const search = async () => {
|
||||||
try {
|
try {
|
||||||
await this.searchAt({x: e.clientX, y: e.clientY});
|
await this.searchAt({x: e.clientX, y: e.clientY}, 'mouse');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.onError(e);
|
this.onError(e);
|
||||||
}
|
}
|
||||||
@ -93,6 +110,14 @@ class Frontend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMouseDown(e) {
|
onMouseDown(e) {
|
||||||
|
if (this.mouseDownPrevent) {
|
||||||
|
this.setMouseDownPrevent(false, false);
|
||||||
|
this.setClickPrevent(true);
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
this.mousePosLast = {x: e.clientX, y: e.clientY};
|
this.mousePosLast = {x: e.clientX, y: e.clientY};
|
||||||
this.popupTimerClear();
|
this.popupTimerClear();
|
||||||
this.searchClear();
|
this.searchClear();
|
||||||
@ -133,6 +158,85 @@ class Frontend {
|
|||||||
this.searchClear();
|
this.searchClear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onClick(e) {
|
||||||
|
if (this.clickPrevent) {
|
||||||
|
this.setClickPrevent(false);
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTouchStart(e) {
|
||||||
|
if (this.primaryTouchIdentifier !== null && this.getIndexOfTouch(e.touches, this.primaryTouchIdentifier) >= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setPrimaryTouch(this.getPrimaryTouch(e.changedTouches));
|
||||||
|
}
|
||||||
|
|
||||||
|
onTouchEnd(e) {
|
||||||
|
if (this.primaryTouchIdentifier === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.getIndexOfTouch(e.changedTouches, this.primaryTouchIdentifier) < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setPrimaryTouch(this.getPrimaryTouch(this.excludeTouches(e.touches, e.changedTouches)));
|
||||||
|
}
|
||||||
|
|
||||||
|
onTouchCancel(e) {
|
||||||
|
this.onTouchEnd(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
onTouchMove(e) {
|
||||||
|
if (!this.scrollPrevent || this.primaryTouchIdentifier === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const touches = e.changedTouches;
|
||||||
|
const index = this.getIndexOfTouch(touches, this.primaryTouchIdentifier);
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const touch = touches[index];
|
||||||
|
this.searchFromTouch(touch.clientX, touch.clientY, 'touchMove');
|
||||||
|
|
||||||
|
e.preventDefault(); // Disable scroll
|
||||||
|
}
|
||||||
|
|
||||||
|
onContextMenu(e) {
|
||||||
|
if (this.contextMenuPrevent) {
|
||||||
|
this.setContextMenuPrevent(false, false);
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onAfterSearch(newRange, type, searched, success) {
|
||||||
|
if (type === 'mouse') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!this.contextMenuChecking ||
|
||||||
|
(this.contextMenuPreviousRange === null ? newRange === null : this.contextMenuPreviousRange.equals(newRange))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'touchStart' && newRange !== null) {
|
||||||
|
this.scrollPrevent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setContextMenuPrevent(true, false);
|
||||||
|
this.setMouseDownPrevent(true, false);
|
||||||
|
this.contextMenuChecking = false;
|
||||||
|
}
|
||||||
|
|
||||||
onBgMessage({action, params}, sender, callback) {
|
onBgMessage({action, params}, sender, callback) {
|
||||||
const handlers = {
|
const handlers = {
|
||||||
optionsSet: options => {
|
optionsSet: options => {
|
||||||
@ -167,18 +271,22 @@ class Frontend {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchAt(point) {
|
async searchAt(point, type) {
|
||||||
if (this.pendingLookup || this.popup.containsPoint(point)) {
|
if (this.pendingLookup || this.popup.containsPoint(point)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const textSource = docRangeFromPoint(point);
|
const textSource = docRangeFromPoint(point);
|
||||||
let hideResults = !textSource || !textSource.containsPoint(point);
|
let hideResults = !textSource || !textSource.containsPoint(point);
|
||||||
|
let searched = false;
|
||||||
|
let success = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!hideResults && (!this.textSourceLast || !this.textSourceLast.equals(textSource))) {
|
if (!hideResults && (!this.textSourceLast || !this.textSourceLast.equals(textSource))) {
|
||||||
|
searched = true;
|
||||||
this.pendingLookup = true;
|
this.pendingLookup = true;
|
||||||
hideResults = !await this.searchTerms(textSource) && !await this.searchKanji(textSource);
|
hideResults = !await this.searchTerms(textSource) && !await this.searchKanji(textSource);
|
||||||
|
success = true;
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (window.yomichan_orphaned) {
|
if (window.yomichan_orphaned) {
|
||||||
@ -196,6 +304,7 @@ class Frontend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.pendingLookup = false;
|
this.pendingLookup = false;
|
||||||
|
this.onAfterSearch(this.textSourceLast, type, searched, success);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -262,6 +371,87 @@ class Frontend {
|
|||||||
|
|
||||||
this.textSourceLast = null;
|
this.textSourceLast = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPrimaryTouch(touchList) {
|
||||||
|
return touchList.length > 0 ? touchList[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getIndexOfTouch(touchList, identifier) {
|
||||||
|
for (let i in touchList) {
|
||||||
|
let t = touchList[i];
|
||||||
|
if (t.identifier === identifier) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
excludeTouches(touchList, excludeTouchList) {
|
||||||
|
const result = [];
|
||||||
|
for (let r of touchList) {
|
||||||
|
if (this.getIndexOfTouch(excludeTouchList, r.identifier) < 0) {
|
||||||
|
result.push(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPrimaryTouch(touch) {
|
||||||
|
if (touch === null) {
|
||||||
|
this.primaryTouchIdentifier = null;
|
||||||
|
this.contextMenuPreviousRange = null;
|
||||||
|
this.contextMenuChecking = false;
|
||||||
|
this.scrollPrevent = false;
|
||||||
|
this.setContextMenuPrevent(false, true);
|
||||||
|
this.setMouseDownPrevent(false, true);
|
||||||
|
this.setClickPrevent(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.primaryTouchIdentifier = touch.identifier;
|
||||||
|
this.contextMenuPreviousRange = this.textSourceLast ? this.textSourceLast.clone() : null;
|
||||||
|
this.contextMenuChecking = true;
|
||||||
|
this.scrollPrevent = false;
|
||||||
|
this.setContextMenuPrevent(false, false);
|
||||||
|
this.setMouseDownPrevent(false, false);
|
||||||
|
this.setClickPrevent(false);
|
||||||
|
|
||||||
|
this.searchFromTouch(touch.clientX, touch.clientY, 'touchStart');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setContextMenuPrevent(value, delay) {
|
||||||
|
if (!delay) {
|
||||||
|
this.contextMenuPrevent = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setMouseDownPrevent(value, delay) {
|
||||||
|
if (!delay) {
|
||||||
|
this.mouseDownPrevent = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setClickPrevent(value) {
|
||||||
|
this.clickPrevent = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
searchFromTouch(x, y, type) {
|
||||||
|
this.popupTimerClear();
|
||||||
|
|
||||||
|
if (!this.options.general.enable || this.pendingLookup) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
try {
|
||||||
|
await this.searchAt({x, y}, type);
|
||||||
|
} catch (e) {
|
||||||
|
this.onError(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
search();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.yomichan_frontend = new Frontend();
|
window.yomichan_frontend = new Frontend();
|
||||||
|
@ -62,6 +62,7 @@ class Popup {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let above = false;
|
||||||
let y = 0;
|
let y = 0;
|
||||||
let height = Math.max(containerHeight, options.general.popupHeight);
|
let height = Math.max(containerHeight, options.general.popupHeight);
|
||||||
const yBelow = elementRect.bottom + options.general.popupOffset;
|
const yBelow = elementRect.bottom + options.general.popupOffset;
|
||||||
@ -75,11 +76,14 @@ class Popup {
|
|||||||
} else {
|
} else {
|
||||||
height = Math.max(height - overflowAbove, 0);
|
height = Math.max(height - overflowAbove, 0);
|
||||||
y = Math.max(yAbove - height, 0);
|
y = Math.max(yAbove - height, 0);
|
||||||
|
above = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
y = yBelow;
|
y = yBelow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.container.classList.toggle('yomichan-float-full-width', options.general.popupDisplayMode === 'full-width');
|
||||||
|
this.container.classList.toggle('yomichan-float-above', above);
|
||||||
this.container.style.left = `${x}px`;
|
this.container.style.left = `${x}px`;
|
||||||
this.container.style.top = `${y}px`;
|
this.container.style.top = `${y}px`;
|
||||||
this.container.style.width = `${width}px`;
|
this.container.style.width = `${width}px`;
|
||||||
|
@ -27,6 +27,10 @@
|
|||||||
}],
|
}],
|
||||||
"minimum_chrome_version": "57.0.0.0",
|
"minimum_chrome_version": "57.0.0.0",
|
||||||
"options_page": "bg/settings.html",
|
"options_page": "bg/settings.html",
|
||||||
|
"options_ui": {
|
||||||
|
"page": "bg/settings.html",
|
||||||
|
"open_in_tab": true
|
||||||
|
},
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"<all_urls>",
|
"<all_urls>",
|
||||||
"storage",
|
"storage",
|
||||||
|
Loading…
Reference in New Issue
Block a user