mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-10 19:05:31 +00:00
110 lines
4 KiB
TypeScript
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;
|
|
}
|
|
}
|
|
}
|
|
}
|