Skip to content

Commit

Permalink
chore(web): re-establishes base-keyboard out-of-bounds roaming
Browse files Browse the repository at this point in the history
  • Loading branch information
jahorton committed Oct 5, 2023
1 parent 4934f8b commit ac24035
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@ import { ViewportZoneSource } from "./viewportZoneSource.js";
export class PaddedZoneSource implements RecognitionZoneSource {
private readonly root: RecognitionZoneSource;

private edgePadding: {
private _edgePadding: {
x: number,
y: number,
w: number,
h: number
};

public get edgePadding() {
return this._edgePadding;
}

/**
* Provides a dynamic 'padded' recognition zone based upon offsetting from the borders
* of the active page's viewport.
Expand Down Expand Up @@ -54,12 +58,32 @@ export class PaddedZoneSource implements RecognitionZoneSource {
// In case it isn't yet defined.
edgePadding = edgePadding || [0, 0, 0, 0];

this.updatePadding(edgePadding);
}

/**
* Provides a dynamic 'padded' recognition zone based upon offsetting from the borders
* of another defined zone.
*
* Padding is defined using the standard CSS border & padding spec style:
* - [a]: equal and even padding on all sides
* - [a, b]: top & bottom use `a`, left & right use `b`
* - [a, b, c]: top uses `a`, left & right use `b`, bottom uses `c`
* - [a, b, c, d]: top, right, bottom, then left.
*
* Positive padding reduces the size of the resulting zone; negative padding expands it.
*
* @param rootZoneSource The root zone source object/element to be 'padded'
* @param edgePadding A set of 1 to 4 numbers defining padding per the standard CSS border & padding spec style.
*/
updatePadding(edgePadding: number[]) {

// Modeled after CSS styling definitions... just with preprocessed numbers, not strings.
switch(edgePadding.length) {
case 1:
// all sides equal
const val = edgePadding[0];
this.edgePadding = {
this._edgePadding = {
x: val,
y: val,
w: 2 * val,
Expand All @@ -68,7 +92,7 @@ export class PaddedZoneSource implements RecognitionZoneSource {
break;
case 2:
// top & bottom, left & right
this.edgePadding = {
this._edgePadding = {
x: edgePadding[1],
y: edgePadding[0],
w: 2 * edgePadding[1],
Expand All @@ -77,15 +101,15 @@ export class PaddedZoneSource implements RecognitionZoneSource {
break;
case 3:
// top, left & right, bottom
this.edgePadding = {
this._edgePadding = {
x: edgePadding[1],
y: edgePadding[0],
w: 2 * edgePadding[1],
h: edgePadding[0] + edgePadding[2]
};
case 4:
// top, right, bottom, left
this.edgePadding = {
this._edgePadding = {
x: edgePadding[3],
y: edgePadding[0],
w: edgePadding[1] + edgePadding[3],
Expand Down
15 changes: 10 additions & 5 deletions web/src/engine/osk/src/keyboard-layout/oskLayerGroup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,17 +116,22 @@ export default class OSKLayerGroup {
}

let row: OSKRow = null;
let bestMatchDistance = Number.MAX_VALUE;

// Find the row that the touch-coordinate lies within.
for(const r of layer.rows) {
const rowRect = translation(r.element.getBoundingClientRect());
if(rowRect.top <= coord.targetY && coord.targetY < rowRect.bottom) {
row = r;
break;
}
}
} else {
const distance = rowRect.top > coord.targetY ? rowRect.top - coord.targetY : coord.targetY - rowRect.bottom;

// If the coordinate isn't even on the keyboard... abort.
if(row == null) {
return null;
if(distance < bestMatchDistance) {
bestMatchDistance = distance;
row = r;
}
}
}

// Assertion: row no longer `null`.
Expand Down
21 changes: 15 additions & 6 deletions web/src/engine/osk/src/visualKeyboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
GestureRecognizer,
GestureRecognizerConfiguration,
GestureSource,
InputSample
InputSample,
PaddedZoneSource
} from '@keymanapp/gesture-recognizer';

import { createStyleSheet, getAbsoluteX, getAbsoluteY, StylesheetManager } from 'keyman/engine/dom-utils';
Expand Down Expand Up @@ -336,11 +337,16 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
}

private constructGestureEngine(): GestureRecognizer<KeyElement, string> {
const rowCount = this.kbdLayout.layerMap['default'].row.length;

const config: GestureRecognizerConfiguration<KeyElement, string> = {
targetRoot: this.element,
// document.body is the event root for mouse interactions b/c we need to track
// when the mouse leaves the VisualKeyboard's hierarchy.
mouseEventRoot: document.body,
// Note: at this point in execution, the value will evaluate to NaN! Height hasn't been set yet.
// BUT: we need to establish the instance now; we can update it later when height _is_ set.
maxRoamingBounds: new PaddedZoneSource(this.element, [-0.333 * this.height / rowCount]),
// touchEventRoot: this.element, // is the default
itemIdentifier: (sample, target) => {
/* ALWAYS use the findNearestKey function.
Expand Down Expand Up @@ -458,6 +464,10 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke

// -- Scratch-space as gestures start becoming integrated --
// Reordering may follow at some point.
//
// Potential long-term idea: only handle the first stage; delegate future stages to
// specialized handlers for the remainder of the sequence.
// Should work for modipresses, too... I think.
if(gestureStage.matchedId == 'special-key-start' && gestureKey.key.spec.baseKeyID == 'K_BKSP') {
// Possible enhancement: maybe update the held location for the backspace if there's movement?
// But... that seems pretty low-priority.
Expand Down Expand Up @@ -1465,8 +1475,6 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
gs.fontSize = this.fontSize.styleString;
bs.fontSize = ParsedLengthStyle.forScalar(fs).styleString;

// NEW CODE ------

// Step 1: have the necessary conditions been met?
const fixedSize = this.width && this.height;
const computedStyle = getComputedStyle(this.kbdDiv);
Expand All @@ -1492,9 +1500,10 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
return;
}

// Step 3: perform layout operations. (Handled by 'old code' section below.)

// END NEW CODE -----------
// Step 3: perform layout operations.
const paddingZone = this.gestureEngine.config.maxRoamingBounds as PaddedZoneSource;
const rowCount = this.currentLayer.rows.length;
paddingZone.updatePadding([-0.333 * this._computedHeight / rowCount]);

// Needs the refreshed layout info to work correctly.
if(this.currentLayer) {
Expand Down

0 comments on commit ac24035

Please sign in to comment.