mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-11 03:15:33 +00:00
Creates fat-finger 'alternates' for use in LMLayer
This commit is contained in:
parent
fb76feae69
commit
2f2ba2de00
5 changed files with 113 additions and 64 deletions
|
|
@ -311,6 +311,10 @@ namespace com.keyman.osk {
|
|||
// generating a probability distribution.
|
||||
this.row.forEach(function(row: ActiveRow): void {
|
||||
row.key.forEach(function(key: ActiveKey): void {
|
||||
// If the key lacks an ID, just skip it. Sometimes used for padding.
|
||||
if(!key.id) {
|
||||
return;
|
||||
}
|
||||
// These represent the within-key distance of the touch from the key's center.
|
||||
// Both should be on the interval [0, 0.5].
|
||||
let dx = Math.abs(touchCoords.x - key.proportionalX);
|
||||
|
|
@ -361,9 +365,9 @@ namespace com.keyman.osk {
|
|||
// Keys usually are specified in a "long form" prefixed with their layer's ID.
|
||||
if(keyId.indexOf(this.id + '-') == 0) {
|
||||
keyId = keyId.replace(this.id + '-', '');
|
||||
|
||||
return this.keyMap[keyId];
|
||||
}
|
||||
|
||||
return this.keyMap[keyId];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -371,10 +375,19 @@ namespace com.keyman.osk {
|
|||
layer: ActiveLayer[];
|
||||
font: string;
|
||||
|
||||
/**
|
||||
* Facilitates mapping layer id strings to their specification objects.
|
||||
*/
|
||||
layerMap: {[layerId: string]: ActiveLayer};
|
||||
|
||||
private constructor() {
|
||||
|
||||
}
|
||||
|
||||
getLayer(layerId: string): ActiveLayer {
|
||||
return this.layerMap[layerId];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param layout
|
||||
|
|
@ -388,6 +401,7 @@ namespace com.keyman.osk {
|
|||
// Create a separate OSK div for each OSK layer, only one of which will ever be visible
|
||||
var n: number, i: number;
|
||||
var layers: LayoutLayer[], layer: LayoutLayer;
|
||||
let layerMap: {[layerId: string]: ActiveLayer} = {};
|
||||
var rows: LayoutRow[];
|
||||
|
||||
layers=layout['layer'];
|
||||
|
|
@ -409,6 +423,7 @@ namespace com.keyman.osk {
|
|||
|
||||
for(n=0; n<layers.length; n++) {
|
||||
ActiveLayer.polyfill(layers[n], formFactor);
|
||||
layerMap[layers[n].id] = layers[n] as ActiveLayer;
|
||||
}
|
||||
|
||||
// Add class functions to the existing layout object, allowing it to act as an ActiveLayout.
|
||||
|
|
@ -419,7 +434,10 @@ namespace com.keyman.osk {
|
|||
}
|
||||
}
|
||||
|
||||
return layout as ActiveLayout;
|
||||
let aLayout = layout as ActiveLayout;
|
||||
aLayout.layerMap = layerMap;
|
||||
|
||||
return aLayout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,28 +26,35 @@ namespace com.keyman.text {
|
|||
readonly token: number;
|
||||
readonly keystroke: KeyEvent;
|
||||
readonly transform: Transform;
|
||||
readonly alternates: Alternate[];
|
||||
readonly preInput: Mock;
|
||||
|
||||
// While not presently needed, there's a chance we'll want to have this information tracked
|
||||
// for developments later down the line. May as well resolve the logic now.
|
||||
readonly removedDks: Deadkey[];
|
||||
readonly insertedDks: Deadkey[];
|
||||
// // While not presently needed, there's a chance we'll want to have this information tracked
|
||||
// // for developments later down the line. May as well resolve the logic now.
|
||||
// readonly removedDks: Deadkey[];
|
||||
// readonly insertedDks: Deadkey[];
|
||||
|
||||
private static tokenSeed: number = 0;
|
||||
|
||||
constructor(keystroke: KeyEvent, transform: Transform, preInput: Mock, removedDks: Deadkey[], insertedDks: Deadkey[]) {
|
||||
constructor(keystroke: KeyEvent, transform: Transform, preInput: Mock, alternates: Alternate[]/*, removedDks: Deadkey[], insertedDks: Deadkey[]*/) {
|
||||
this.token = Transcription.tokenSeed++;
|
||||
|
||||
this.keystroke = keystroke;
|
||||
this.transform = transform;
|
||||
this.alternates = alternates;
|
||||
this.preInput = preInput;
|
||||
this.removedDks = removedDks;
|
||||
this.insertedDks = insertedDks;
|
||||
// this.removedDks = removedDks;
|
||||
// this.insertedDks = insertedDks;
|
||||
|
||||
this.transform.id = this.token;
|
||||
}
|
||||
}
|
||||
|
||||
export interface Alternate {
|
||||
t: Transform;
|
||||
p: number;
|
||||
}
|
||||
|
||||
export abstract class OutputTarget {
|
||||
private _dks: text.DeadkeyTracker;
|
||||
|
||||
|
|
@ -122,46 +129,46 @@ namespace com.keyman.text {
|
|||
return new TextTransform(delta, deletedLeft, originalRight - undeletedRight);
|
||||
}
|
||||
|
||||
buildTranscriptionFrom(original: OutputTarget, keyEvent: KeyEvent): Transcription {
|
||||
buildTranscriptionFrom(original: OutputTarget, keyEvent: KeyEvent, alternates?: Alternate[]): Transcription {
|
||||
let transform = this.buildTransformFrom(original);
|
||||
|
||||
// While not presently needed, there's a chance we'll want to have Deadkey mutation tracked
|
||||
// for developments later down the line. May as well resolve the logic now.
|
||||
// // While not presently needed, there's a chance we'll want to have Deadkey mutation tracked
|
||||
// // for developments later down the line. May as well resolve the logic now.
|
||||
|
||||
// Determine what deadkeys were removed and added.
|
||||
let fromDks = original.deadkeys().toSortedArray();
|
||||
let toDks = this.deadkeys().toSortedArray();
|
||||
// // Determine what deadkeys were removed and added.
|
||||
// let fromDks = original.deadkeys().toSortedArray();
|
||||
// let toDks = this.deadkeys().toSortedArray();
|
||||
|
||||
let commonDks: Deadkey[] = [];
|
||||
let removedDks: Deadkey[] = [];
|
||||
let insertedDks: Deadkey[] = [];
|
||||
// let commonDks: Deadkey[] = [];
|
||||
// let removedDks: Deadkey[] = [];
|
||||
// let insertedDks: Deadkey[] = [];
|
||||
|
||||
// Since the ordinals of any original deadkey are preserved, we can check for the (id, ordinal).
|
||||
fromDks.forEach(function(dk: Deadkey){
|
||||
for(var i=0; i < toDks.length; i++) {
|
||||
if(toDks[i].d == dk.d && toDks[i].o == dk.o) {
|
||||
commonDks.push(dk);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// // Since the ordinals of any original deadkey are preserved, we can check for the (id, ordinal).
|
||||
// fromDks.forEach(function(dk: Deadkey){
|
||||
// for(var i=0; i < toDks.length; i++) {
|
||||
// if(toDks[i].d == dk.d && toDks[i].o == dk.o) {
|
||||
// commonDks.push(dk);
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Wasn't found to be in common? Must have been removed.
|
||||
removedDks.push(dk);
|
||||
});
|
||||
// // Wasn't found to be in common? Must have been removed.
|
||||
// removedDks.push(dk);
|
||||
// });
|
||||
|
||||
toDks.forEach(function(dk: Deadkey) {
|
||||
for(var i=0; i < commonDks.length; i++) {
|
||||
if(commonDks[i].d == dk.d && commonDks[i].o == dk.o) {
|
||||
// It's a common one.
|
||||
return;
|
||||
}
|
||||
}
|
||||
// toDks.forEach(function(dk: Deadkey) {
|
||||
// for(var i=0; i < commonDks.length; i++) {
|
||||
// if(commonDks[i].d == dk.d && commonDks[i].o == dk.o) {
|
||||
// // It's a common one.
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Wasn't found to be in common? Must have been inserted.
|
||||
insertedDks.push(dk);
|
||||
});
|
||||
// // Wasn't found to be in common? Must have been inserted.
|
||||
// insertedDks.push(dk);
|
||||
// });
|
||||
|
||||
return new Transcription(keyEvent, transform, Mock.from(original), removedDks, insertedDks);
|
||||
return new Transcription(keyEvent, transform, Mock.from(original), alternates/*, removedDks, insertedDks*/);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -313,6 +313,8 @@ namespace com.keyman.text.prediction {
|
|||
this.recordTranscription(transcription);
|
||||
|
||||
let transform = transcription.transform;
|
||||
// TODO: Use this instead of the single Transform; requires LMLayer work.
|
||||
let alternates = transcription.alternates;
|
||||
var promise = this.currentPromise = this.lmEngine.predict(transform, context);
|
||||
|
||||
let mm = this;
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ namespace com.keyman.text {
|
|||
* @param {boolean} usingOSK
|
||||
* @return {string}
|
||||
*/
|
||||
defaultKeyOutput(Lkc: KeyEvent, keyShiftState: number, usingOSK: boolean): string {
|
||||
defaultKeyOutput(Lkc: KeyEvent, keyShiftState: number, usingOSK: boolean, disableDOM: boolean): string {
|
||||
var Lkc: KeyEvent;
|
||||
|
||||
let keyName = Lkc.kName;
|
||||
|
|
@ -73,13 +73,19 @@ namespace com.keyman.text {
|
|||
keyman.interface.defaultBackspace();
|
||||
return '';
|
||||
case Codes.keyCodes['K_TAB']:
|
||||
domManager.moveToNext(keyShiftState);
|
||||
if(!disableDOM) {
|
||||
domManager.moveToNext(keyShiftState);
|
||||
}
|
||||
break;
|
||||
case Codes.keyCodes['K_TABBACK']:
|
||||
domManager.moveToNext(true);
|
||||
if(!disableDOM) {
|
||||
domManager.moveToNext(true);
|
||||
}
|
||||
break;
|
||||
case Codes.keyCodes['K_TABFWD']:
|
||||
domManager.moveToNext(false);
|
||||
if(!disableDOM) {
|
||||
domManager.moveToNext(false);
|
||||
}
|
||||
break;
|
||||
case Codes.keyCodes['K_ENTER']:
|
||||
// Insert new line in text area fields
|
||||
|
|
@ -95,12 +101,13 @@ namespace com.keyman.text {
|
|||
} else if(typeof(Lelem.base) != 'undefined' && dom.Utils.instanceof(Lelem.base, "HTMLInputElement")) {
|
||||
inputEle = <HTMLInputElement> Lelem.base;
|
||||
}
|
||||
|
||||
if (inputEle && (inputEle.type == 'search' || inputEle.type == 'submit')) {
|
||||
inputEle.disabled=false;
|
||||
inputEle.form.submit();
|
||||
} else {
|
||||
domManager.moveToNext(false);
|
||||
if(!disableDOM) {
|
||||
if (inputEle && (inputEle.type == 'search' || inputEle.type == 'submit')) {
|
||||
inputEle.disabled=false;
|
||||
inputEle.form.submit();
|
||||
} else {
|
||||
domManager.moveToNext(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
@ -302,8 +309,7 @@ namespace com.keyman.text {
|
|||
}
|
||||
}
|
||||
|
||||
processKeystroke(keyEvent: KeyEvent, outputTarget: OutputTarget, fromOSK: boolean): boolean {
|
||||
// Create `Processor.processKeystroke` for this section.
|
||||
processKeystroke(keyEvent: KeyEvent, outputTarget: OutputTarget, fromOSK: boolean, disableDOM: boolean): boolean {
|
||||
let keyman = com.keyman.singleton;
|
||||
|
||||
var activeKeyboard = keyman.keyboardManager.activeKeyboard;
|
||||
|
|
@ -333,7 +339,7 @@ namespace com.keyman.text {
|
|||
|
||||
// Handle unmapped keys, including special keys
|
||||
// The following is physical layout dependent, so should be avoided if possible. All keys should be mapped.
|
||||
var ch = this.defaultKeyOutput(keyEvent, keyEvent.Lmodifiers, true);
|
||||
var ch = this.defaultKeyOutput(keyEvent, keyEvent.Lmodifiers, true, disableDOM);
|
||||
if(ch) {
|
||||
kbdInterface.output(0, outputTarget, ch);
|
||||
LeventMatched = 1;
|
||||
|
|
@ -342,7 +348,6 @@ namespace com.keyman.text {
|
|||
}
|
||||
}
|
||||
|
||||
/// End serious keystroke processing.
|
||||
return LeventMatched == 1;
|
||||
}
|
||||
|
||||
|
|
@ -355,7 +360,6 @@ namespace com.keyman.text {
|
|||
*/
|
||||
processKeyEvent(keyEvent: KeyEvent, e?: osk.KeyElement | boolean): boolean {
|
||||
let keyman = com.keyman.singleton;
|
||||
//var Lelem = keyman.domManager.getLastActiveElement();
|
||||
|
||||
let fromOSK = !!e; // If specified, it's from the OSK.
|
||||
|
||||
|
|
@ -364,8 +368,6 @@ namespace com.keyman.text {
|
|||
e = null as osk.KeyElement; // Cast is necessary for TS type-checking later in the method.
|
||||
}
|
||||
|
||||
var activeKeyboard = keyman.keyboardManager.activeKeyboard;
|
||||
let kbdInterface = keyman.interface;
|
||||
let formFactor = keyman.util.device.formFactor;
|
||||
let keyMapManager = keyman.keyMapManager;
|
||||
|
||||
|
|
@ -442,9 +444,7 @@ namespace com.keyman.text {
|
|||
let outputTarget = Processor.getOutputTarget(keyEvent.Ltarg);
|
||||
let preInputMock = Mock.from(outputTarget);
|
||||
|
||||
// For fat-finger adjustments, we should iterate across the most likely members of the key distribution,
|
||||
// not just the selected key.
|
||||
let LeventMatched = this.processKeystroke(keyEvent, outputTarget, fromOSK);
|
||||
let LeventMatched = this.processKeystroke(keyEvent, outputTarget, fromOSK, false);
|
||||
|
||||
// End of fat-finger loop section; the rest is post-processing maintenance.
|
||||
|
||||
|
|
@ -455,7 +455,28 @@ namespace com.keyman.text {
|
|||
|
||||
// Should we swallow any further processing of keystroke events for this keydown-keypress sequence?
|
||||
if(LeventMatched) {
|
||||
let transcription = outputTarget.buildTranscriptionFrom(preInputMock, keyEvent);
|
||||
let alternates: Alternate[];
|
||||
|
||||
if(keyEvent.keyDistribution) {
|
||||
let activeLayout = keyman['osk'].vkbd.layout as osk.ActiveLayout;
|
||||
alternates = [];
|
||||
|
||||
for(let pair of keyEvent.keyDistribution) {
|
||||
let mock = Mock.from(preInputMock);
|
||||
|
||||
let altKey = activeLayout.getLayer(keyEvent.kbdLayer).getKey(pair.keyId);
|
||||
if(!altKey) {
|
||||
console.warn("Potential fat-finger key could not be found in layer!");
|
||||
continue;
|
||||
}
|
||||
let altEvent = this._GetClickEventProperties(altKey, keyEvent.Ltarg);
|
||||
if(this.processKeystroke(altEvent, mock, fromOSK, true)) {
|
||||
alternates.push({t: mock.buildTransformFrom(preInputMock), 'p': pair.p});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let transcription = outputTarget.buildTranscriptionFrom(preInputMock, keyEvent, alternates);
|
||||
// Notify the ModelManager of new input
|
||||
keyman.modelManager.predict(transcription);
|
||||
|
||||
|
|
@ -535,7 +556,7 @@ namespace com.keyman.text {
|
|||
|
||||
mappingEvent.kName = 'K_xxxx';
|
||||
mappingEvent.Ltarg = null;
|
||||
var mappedChar: string = this.defaultKeyOutput(Lkc, (shifted ? 0x10 : 0), false);
|
||||
var mappedChar: string = this.defaultKeyOutput(Lkc, (shifted ? 0x10 : 0), false, true);
|
||||
if(mappedChar) {
|
||||
// FIXME; Warning - will return 96 for 'a', which is a keycode corresponding to Codes.keyCodes('K_NP1') - a numpad key.
|
||||
Lkc.Lcode = mappedChar.charCodeAt(0);
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ describe("Transcriptions and Transforms", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("Operations with deadkeys", function() {
|
||||
/*describe("Operations with deadkeys", function() {
|
||||
// Just one, less nuanced/subdivided; it's not a present priority for our work, but it should provide a decent basis if/when it's needed.
|
||||
it("Correctly recognizes deadkey set mutations", function() {
|
||||
var Mock = com.keyman.text.Mock;
|
||||
|
|
@ -193,4 +193,5 @@ describe("Transcriptions and Transforms", function() {
|
|||
assert.deepEqual({d: ins[0].d, p: ins[0].p}, {d: 3, p:2}, "Selected wrong deadkey as inserted");
|
||||
});
|
||||
});
|
||||
*/
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue