spiegel-keyman/web/source/osk/embedded/subkeyDelegator.ts
2021-07-30 08:15:16 +07:00

110 lines
4 KiB
TypeScript

/// <reference path="../realizedGesture.interface.ts" />
namespace com.keyman.osk.embedded {
/**
* As the subkey popup view is handled by the host app when in embedded mode
* within our Android app, this class represents the fact that KMW has
* "delegated" subkey UI and selection to the host app. Hence, "Delegator",
* rather than "Popup".
*
* The `resolve` method should be triggered, in some fashion, by the host app
* whenever the user has completed their longpress, potentially selecting
* a subkey.
*
* This class will also track the ongoing touch event in case the base key is
* reselected, which _is_ managed by this class, not the host app.
*/
export class SubkeyDelegator implements RealizedGesture {
private resolver: (keyEvent: text.KeyEvent) => void;
private readonly vkbd: VisualKeyboard;
public readonly baseKey: KeyElement;
public readonly promise: Promise<text.KeyEvent>;
private movedFromBaseKey: boolean = false;
private baseKeySelected: boolean = false;
constructor(vkbd: VisualKeyboard, e: KeyElement) {
this.vkbd = vkbd;
let _this = this;
this.promise = new Promise<text.KeyEvent>(function(resolve) {
_this.resolver = resolve;
});
this.baseKey = e;
}
/**
* Resolves the ongoing longpress -> subkey gesture, fulfilling this
* `SubkeyDelegator`'s `promise` of a `KeyEvent`.
*
* If no subkey is selected but the original base key is, `resolve(null)`
* will return a key event corresponding to the base key.
*
* @param keyCoreID {string} The 'core ID' (id + modifier layer) of
* a selected subkey. May be `null`.
*/
public resolve(keyCoreID: string) {
if(this.resolver) {
let keyEvent: text.KeyEvent = null;
if(keyCoreID == null && this.baseKeySelected) {
// Handle selection of base key underneath the subkey array.
keyEvent = this.vkbd.keyEventFromSpec(this.baseKey.key.spec as keyboards.ActiveKey, null);
this.baseKey.key.highlight(false);
} else if(keyCoreID != null) {
// This is set with the base key of our current subkey elsewhere within the engine.
let baseKey: OSKKeySpec = this.baseKey.key.spec;
let selectedKey: OSKKeySpec;
if(baseKey.coreID == keyCoreID) {
selectedKey = baseKey;
} else {
// ... yeah, there are some funky type shenanigans between the two.
// OSKKeySpec is the OSK's... reinterpretation of the ActiveKey type.
selectedKey = (baseKey as keyboards.ActiveKey).getSubkey(keyCoreID) as OSKKeySpec;
}
if(!selectedKey) {
// While we can't complete successfully, the subkey operation is done; we
// should still signal that and update related gesture state management.
this.resolver(null);
console.error("Could not find subkey '" + keyCoreID + "' under base key '" + baseKey.coreID + "'!");
return;
}
keyEvent = this.vkbd.keyEventFromSpec(selectedKey as keyboards.ActiveKey, null);
keyEvent.vkCode=keyEvent.Lcode;
} // else /* if(keyCoreID == null) */ keyEvent = null; // As initialized at the top.
this.resolver(keyEvent);
}
this.resolver = null;
}
public isVisible(): boolean {
return true;
}
public clear() {
// no-op; it's fully controlled on the app side.
}
/**
* Allows this class to detect if the user may have changed their mind and
* re-selected the base key.
* @param touch
*/
updateTouch(input: InputEventCoordinate) {
this.baseKeySelected = this.baseKey.key.isUnderTouch(input);
// Prevent highlighting & selection before the touch has moved from the base key.
if(this.movedFromBaseKey) {
this.baseKey.key.highlight(this.baseKeySelected);
} else {
this.movedFromBaseKey = !this.baseKeySelected;
}
}
}
}