mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-07 22:47:29 +00:00
Enhances 'nearest target' checks and auto-cancels.
This commit is contained in:
parent
36c9d17c58
commit
05c8d734f8
4 changed files with 90 additions and 82 deletions
41
common/predictive-text/package-lock.json
generated
41
common/predictive-text/package-lock.json
generated
|
|
@ -1177,8 +1177,7 @@
|
|||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.2.0",
|
||||
|
|
@ -1199,14 +1198,12 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
|
@ -1221,20 +1218,17 @@
|
|||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
|
@ -1351,8 +1345,7 @@
|
|||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.5",
|
||||
|
|
@ -1364,7 +1357,6 @@
|
|||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"number-is-nan": "^1.0.0"
|
||||
}
|
||||
|
|
@ -1379,7 +1371,6 @@
|
|||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
|
@ -1387,14 +1378,12 @@
|
|||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"minipass": {
|
||||
"version": "2.2.4",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.1.1",
|
||||
"yallist": "^3.0.0"
|
||||
|
|
@ -1413,7 +1402,6 @@
|
|||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
|
|
@ -1494,8 +1482,7 @@
|
|||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
|
@ -1507,7 +1494,6 @@
|
|||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
|
|
@ -1593,8 +1579,7 @@
|
|||
"safe-buffer": {
|
||||
"version": "5.1.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
|
|
@ -1630,7 +1615,6 @@
|
|||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"code-point-at": "^1.0.0",
|
||||
"is-fullwidth-code-point": "^1.0.0",
|
||||
|
|
@ -1650,7 +1634,6 @@
|
|||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^2.0.0"
|
||||
}
|
||||
|
|
@ -1694,14 +1677,12 @@
|
|||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"yallist": {
|
||||
"version": "3.0.2",
|
||||
"bundled": true,
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -87,17 +87,20 @@ namespace com.keyman.dom {
|
|||
*
|
||||
* @param {Event} e touch event
|
||||
* @param {Object} t HTML object at touch point
|
||||
* @param {boolean} omitCurrent Omits any target directly under the touch point.
|
||||
* @return {Object} nearest key to touch point
|
||||
*
|
||||
**/
|
||||
findNearestTarget(e: TouchEvent, t: HTMLElement): Target {
|
||||
if((!e) || (typeof e.changedTouches == 'undefined')
|
||||
|| (e.changedTouches.length == 0)) {
|
||||
private findTargetFromTouch(e: TouchEvent, t: HTMLElement, forMove: boolean): Target {
|
||||
let touchList = forMove ? e.touches : e.changedTouches;
|
||||
|
||||
if((!e) || (typeof touchList == 'undefined')
|
||||
|| (touchList.length == 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Get touch point on screen
|
||||
var x = e.changedTouches[0].pageX;
|
||||
var x = touchList[0].pageX;
|
||||
|
||||
// Get the UI row beneath touch point (SuggestionBanner div, 'kmw-key-row' if OSK, ...)
|
||||
while(t && t.className !== undefined && t.className.indexOf(this.rowClassMatch) < 0) {
|
||||
|
|
@ -108,24 +111,35 @@ namespace com.keyman.dom {
|
|||
}
|
||||
|
||||
// Find minimum distance from any key
|
||||
var k: number, bestMatch=0, dx: number, dxMax=24, dxMin=100000, x1: number, x2: number;
|
||||
var k: number, bestMatch=0, dxMax=24, dxMin=100000, x1: number, x2: number;
|
||||
for(k = 0; k < t.childNodes.length; k++) {
|
||||
let childNode = t.childNodes[k] as HTMLElement;
|
||||
/* // If integrating with the OSK, replace with an 'isInvalidTarget' check.
|
||||
if(childNode.className !== undefined && childNode.className.indexOf('key-hidden') >= 0) {
|
||||
|
||||
if(this.isInvalidTarget(this.findTargetFrom(childNode))) {
|
||||
continue;
|
||||
}*/
|
||||
}
|
||||
|
||||
x1 = childNode.offsetLeft;
|
||||
x2 = x1 + childNode.offsetWidth;
|
||||
dx = x1 - x;
|
||||
if(dx >= 0 && dx < dxMin) {
|
||||
|
||||
// If it lies completely to the right and is the closest so far
|
||||
let dxRight = x1 - x;
|
||||
if(dxRight >= 0 && dxRight < dxMin) {
|
||||
bestMatch = k;
|
||||
dxMin = dx;
|
||||
dxMin = dxRight;
|
||||
}
|
||||
dx = x - x2;
|
||||
if(dx >= 0 && dx < dxMin) {
|
||||
|
||||
// If it lies completely to the left and is the closest so far
|
||||
let dxLeft = x - x2;
|
||||
if(dxLeft >= 0 && dxLeft < dxMin) {
|
||||
bestMatch = k;
|
||||
dxMin = dx;
|
||||
dxMin = dxLeft;
|
||||
}
|
||||
|
||||
// If it is neither completely to the left nor completely to the right,
|
||||
// it's under the cursor. Stop the search!
|
||||
if(dxLeft < 0 && dxRight < 0) {
|
||||
return this.findTargetFrom(childNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -140,17 +154,46 @@ namespace com.keyman.dom {
|
|||
}
|
||||
|
||||
if(((x1 - x) >= 0 && (x1 - x) < dxMax) || ((x - x2) >= 0 && (x - x2) < dxMax)) {
|
||||
// If integrating with the OSK, should probably use `findTargetFrom`.
|
||||
// The OSK would want this (key-square) element's child.
|
||||
return <Target> t;
|
||||
return this.findTargetFrom(t);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
findBestTarget(e: TouchEvent, forMove?: boolean) {
|
||||
var eventTarget: HTMLElement;
|
||||
|
||||
if(forMove) {
|
||||
eventTarget = document.elementFromPoint(e.touches[0].clientX, e.touches[0].clientY) as HTMLElement;
|
||||
} else {
|
||||
eventTarget = e.changedTouches[0].target as HTMLElement;
|
||||
}
|
||||
|
||||
let target = this.findTargetFrom(eventTarget);
|
||||
|
||||
// Should refactor this multi-check a bit for more overall reliability.
|
||||
if(!target) {
|
||||
// We didn't find a direct target, so we should look for the closest possible one.
|
||||
// Filters out invalid targets.
|
||||
target = this.findTargetFromTouch(e, eventTarget, forMove);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reports whether or not a `Target` should be considered invalid. Needed by the OSK for
|
||||
* hidden keys.
|
||||
* @param target A `Target` element to be validated.
|
||||
*/
|
||||
protected isInvalidTarget(target: Target): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
touchStart(e: TouchEvent) {
|
||||
// Determine the selected Target, manage state.
|
||||
this.currentTarget = this.findTargetFrom(e.changedTouches[0].target as HTMLElement);
|
||||
//this.currentTarget = this.findTargetFrom(e.changedTouches[0].target as HTMLElement);
|
||||
this.currentTarget = this.findBestTarget(e);
|
||||
this.touchX = e.changedTouches[0].pageX;
|
||||
this.touchY = e.changedTouches[0].pageY;
|
||||
|
||||
|
|
@ -158,16 +201,6 @@ namespace com.keyman.dom {
|
|||
|
||||
this.touchCount = e.touches.length;
|
||||
|
||||
// If option should not be selectable, how do we re-target?
|
||||
// (If/when the OSK is refactored to use this, we'll need appropriate code here.)
|
||||
// Probably a 'isInvalidTarget()' and 'nearestTarget()'.
|
||||
|
||||
if(!this.currentTarget) {
|
||||
// Find nearest target.
|
||||
this.currentTarget = this.findNearestTarget(e, <HTMLElement> e.changedTouches[0].target)
|
||||
}
|
||||
|
||||
// Still no appropriate target? Reject the touch event.
|
||||
if(!this.currentTarget) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -234,12 +267,7 @@ namespace com.keyman.dom {
|
|||
this.pendingTarget = null;
|
||||
// Always clear highlighting of current target on release (multi-touch)
|
||||
} else {
|
||||
var tt = e.changedTouches[0];
|
||||
t = this.findTargetFrom(tt.target as HTMLElement);
|
||||
if(!t) {
|
||||
var t1 = document.elementFromPoint(tt.clientX,tt.clientY);
|
||||
t = this.findNearestTarget(e, <HTMLElement> t1);
|
||||
}
|
||||
t = this.findBestTarget(e);
|
||||
|
||||
if(t) {
|
||||
this.highlight(t,false);
|
||||
|
|
@ -272,22 +300,14 @@ namespace com.keyman.dom {
|
|||
}
|
||||
|
||||
// Get touch position
|
||||
var x=typeof e.touches == 'object' ? e.touches[0].clientX : e.clientX,
|
||||
y=typeof e.touches == 'object' ? e.touches[0].clientY : e.clientY;
|
||||
var y=typeof e.touches == 'object' ? e.touches[0].clientY : e.clientY;
|
||||
|
||||
// Move target key and highlighting
|
||||
var t1 = <HTMLElement> document.elementFromPoint(x,y),
|
||||
key0 = this.pendingTarget,
|
||||
key1 = this.findTargetFrom(t1); // For the OSK, this ALSO gets subkeys.
|
||||
var key0 = this.pendingTarget,
|
||||
key1 = this.findBestTarget(e, true); // For the OSK, this ALSO gets subkeys.
|
||||
|
||||
// If option should not be selectable, how do we re-target?
|
||||
// (If/when the OSK is refactored to use this, we'll need appropriate code here.)
|
||||
// Probably a 'isInvalidTarget()' and 'nearestTarget()'.
|
||||
|
||||
// Find the nearest key to the touch point if not on a visible key
|
||||
if(!key1) {
|
||||
key1 = this.findNearestTarget(e,t1);
|
||||
}
|
||||
|
||||
// Do not move over keys if device popup visible
|
||||
if(this.hasModalPopup()) {
|
||||
|
|
@ -349,7 +369,11 @@ namespace com.keyman.dom {
|
|||
// Cancel touch if moved up and off keyboard, unless popup keys visible
|
||||
} else {
|
||||
let base = this.baseElement;
|
||||
if(key0 && e.touches[0].pageY < Math.max(5, base.offsetTop - 0.25 * base.offsetHeight)) {
|
||||
let top = (base.offsetParent as HTMLElement).offsetTop + base.offsetTop;
|
||||
let height = base.offsetHeight;
|
||||
let yMin = Math.max(5, top - 0.25 * height);
|
||||
let yMax = (top + height) + 0.25 * height;
|
||||
if(key0 && (e.touches[0].pageY < yMin || e.touches[0].pageY > yMax)) {
|
||||
this.highlight(key0,false);
|
||||
this.clearHolds();
|
||||
this.pendingTarget = null;
|
||||
|
|
|
|||
|
|
@ -291,9 +291,9 @@ namespace com.keyman.osk {
|
|||
if(e.parentNode && util.hasClass(<HTMLElement> e.parentNode,'kmw-suggest-option')) {
|
||||
return e.parentNode as HTMLDivElement;
|
||||
}
|
||||
if(e.firstChild && util.hasClass(<HTMLElement> e.firstChild,'kmw-suggest-option')) {
|
||||
return e.firstChild as HTMLDivElement;
|
||||
}
|
||||
// if(e.firstChild && util.hasClass(<HTMLElement> e.firstChild,'kmw-suggest-option')) {
|
||||
// return e.firstChild as HTMLDivElement;
|
||||
// }
|
||||
}
|
||||
} catch(ex) {}
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -1062,9 +1062,12 @@ namespace com.keyman.osk {
|
|||
}
|
||||
// Cancel touch if moved up and off keyboard, unless popup keys visible
|
||||
} else {
|
||||
let _Box = com.keyman.singleton.osk._Box;
|
||||
var yMin = Math.max(5, _Box.offsetTop - 0.25*_Box.offsetHeight);
|
||||
if(key0 && e.touches[0].pageY < Math.max(5,_Box.offsetTop - 0.25*_Box.offsetHeight)) {
|
||||
// _Box has (most of) the useful client values.
|
||||
let _Box = this.kbdDiv.offsetParent as HTMLElement; // == osk._Box
|
||||
let height = (this.kbdDiv.firstChild as HTMLElement).offsetHeight; // firstChild == layer-group, has height info.
|
||||
// We need to adjust the offset properties by any offsets related to the active banner.
|
||||
var yMin = Math.max(5, this.kbdDiv.offsetTop + _Box.offsetTop - 0.25*height);
|
||||
if(key0 && e.touches[0].pageY < yMin) {
|
||||
this.highlightKey(key0,false);
|
||||
this.showKeyTip(null,false);
|
||||
this.keyPending = null;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue