From 115ab18d71fdb51a6714325f6a68ded1bdf6658b Mon Sep 17 00:00:00 2001 From: Eberhard Beilharz Date: Thu, 16 Apr 2026 22:07:58 +0200 Subject: [PATCH] refactor(web): add abstract `Keyboard` base class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also rename `JSKeyboardData` → `KeyboardData`. Test-bot: skip --- web/src/app/browser/src/keymanEngine.ts | 14 ++- .../src/js-processor/jsKeyboardInterface.ts | 25 +++--- .../src/js-processor/jsKeyboardProcessor.ts | 2 +- .../keyboard-storage/stubAndKeyboardCache.ts | 19 ++-- web/src/engine/src/keyboard/index.ts | 3 +- web/src/engine/src/keyboard/keyEvent.ts | 2 +- .../src/keyboard/keyboards/jsKeyboard.ts | 4 +- .../engine/src/keyboard/keyboards/keyboard.ts | 89 +++++++++++++++++++ .../src/keyboard/keyboards/keyboardHarness.ts | 4 +- .../keyboard/keyboards/keyboardLoaderBase.ts | 7 +- .../keyboards/keyboardMinimalInterface.ts | 2 +- .../keyboard/keyboards/keyboardProcessor.ts | 2 +- .../src/keyboard/keyboards/kmxKeyboard.ts | 21 ++++- .../keyboards/loaders/domKeyboardLoader.ts | 4 +- .../src/main/headless/inputProcessor.ts | 4 +- web/src/engine/src/main/keymanEngineBase.ts | 4 +- web/src/engine/src/osk/components/titleBar.ts | 4 +- web/src/engine/src/osk/index.ts | 2 +- web/src/engine/src/osk/views/oskView.ts | 13 +-- web/src/engine/src/osk/visualKeyboard.ts | 2 +- web/src/test/auto/dom/kbdLoader.ts | 10 +-- .../coreKeyboardProcessor.tests.ts | 4 +- .../specialized-backspace.tests.ts | 6 +- .../resources/loader/nodeKeyboardLoader.ts | 4 +- 24 files changed, 180 insertions(+), 71 deletions(-) create mode 100644 web/src/engine/src/keyboard/keyboards/keyboard.ts diff --git a/web/src/app/browser/src/keymanEngine.ts b/web/src/app/browser/src/keymanEngine.ts index 6e9f8ebe33..b9663ca374 100644 --- a/web/src/app/browser/src/keymanEngine.ts +++ b/web/src/app/browser/src/keymanEngine.ts @@ -689,18 +689,14 @@ export class KeymanEngine extends KeymanEngineBase boolean, textStore: TextStore, keystroke: KeyEvent, readonly: boolean): ProcessorAction { @@ -1057,7 +1062,7 @@ export class JSKeyboardInterface extends KeyboardHarness { const preInput = SyntheticTextStore.from(textStore, true); // Capture the initial state of any variable stores - const cachedVariableStores = this.activeKeyboard.variableStores; + const cachedVariableStores = this.activeJSKeyboard.variableStores; // Establishes the results object, allowing corresponding commands to set values here as appropriate. this.ruleBehavior = new ProcessorAction(); @@ -1077,8 +1082,8 @@ export class JSKeyboardInterface extends KeyboardHarness { // We always backup the changes to variable stores to the ProcessorAction, to // be applied during finalization, then restore them to the cached initial // values to avoid side-effects with predictive text mocks. - this.ruleBehavior.variableStores = this.activeKeyboard.variableStores; - this.activeKeyboard.variableStores = cachedVariableStores; + this.ruleBehavior.variableStores = this.activeJSKeyboard.variableStores; + this.activeJSKeyboard.variableStores = cachedVariableStores; // `matched` refers to whether or not the FINAL rule (from any group) matched, rather than // whether or not ANY rule matched. If the final rule doesn't match, we trigger the key's @@ -1104,7 +1109,7 @@ export class JSKeyboardInterface extends KeyboardHarness { * keyboard */ applyVariableStores(stores: VariableStoreDictionary): void { - this.activeKeyboard.variableStores = stores; + this.activeJSKeyboard.variableStores = stores; } /** diff --git a/web/src/engine/src/js-processor/jsKeyboardProcessor.ts b/web/src/engine/src/js-processor/jsKeyboardProcessor.ts index 757819fb19..3eb5a7ab36 100644 --- a/web/src/engine/src/js-processor/jsKeyboardProcessor.ts +++ b/web/src/engine/src/js-processor/jsKeyboardProcessor.ts @@ -68,7 +68,7 @@ export class JSKeyboardProcessor extends EventEmitter implements Keybo } public get activeKeyboard(): JSKeyboard { - return this.keyboardInterface.activeKeyboard; + return this.keyboardInterface.activeKeyboard as JSKeyboard; } public set activeKeyboard(keyboard: JSKeyboard) { diff --git a/web/src/engine/src/keyboard-storage/stubAndKeyboardCache.ts b/web/src/engine/src/keyboard-storage/stubAndKeyboardCache.ts index 1537a8cc52..98c4a237b1 100644 --- a/web/src/engine/src/keyboard-storage/stubAndKeyboardCache.ts +++ b/web/src/engine/src/keyboard-storage/stubAndKeyboardCache.ts @@ -1,4 +1,4 @@ -import { type Keyboard, JSKeyboard, KeyboardLoaderBase as KeyboardLoader, KMXKeyboard } from "keyman/engine/keyboard"; +import { Keyboard, JSKeyboard, KeyboardLoaderBase as KeyboardLoader, KMXKeyboard } from "keyman/engine/keyboard"; import { EventEmitter } from "eventemitter3"; import { KeyboardStub } from "./keyboardStub.js"; @@ -152,7 +152,7 @@ export class StubAndKeyboardCache extends EventEmitter { keyboardID = toPrefixedKeyboardId(keyboardID); const cachedEntry = this.keyboardTable[keyboardID]; - if(cachedEntry instanceof JSKeyboard) { + if(cachedEntry instanceof Keyboard) { return Promise.resolve(cachedEntry); } else if(cachedEntry instanceof Promise) { return cachedEntry; @@ -172,6 +172,7 @@ export class StubAndKeyboardCache extends EventEmitter { promise.then((kbd) => { // Overrides the built-in ID in case of keyboard namespacing. + // TODO-web-core: what do we have to do for KMX keyboards here? if (kbd instanceof JSKeyboard) { kbd.scriptObject["KI"] = keyboardID; } @@ -197,12 +198,12 @@ export class StubAndKeyboardCache extends EventEmitter { } getStub(keyboardID: string, languageID: string): KeyboardStub; - getStub(keyboard: JSKeyboard, languageID?: string): KeyboardStub; - getStub(arg0: string | JSKeyboard, arg1?: string): KeyboardStub { + getStub(keyboard: Keyboard, languageID?: string): KeyboardStub; + getStub(arg0: string | Keyboard, arg1?: string): KeyboardStub { let keyboardID: string; const languageID = arg1 || '---'; - if(arg0 instanceof JSKeyboard) { + if(arg0 instanceof Keyboard) { keyboardID = arg0.id; } else { keyboardID = arg0; @@ -229,12 +230,12 @@ export class StubAndKeyboardCache extends EventEmitter { /** * Removes all metadata (stubs) associated with a specific keyboard from the cache, optionally * removing the cached keyboard as well. - * @param keyboard Either the keyboard ID or `JSKeyboard` instance - * @param purge If `true`, will also purge the `JSKeyboard` instance itself from the cache. + * @param keyboard Either the keyboard ID or `Keyboard` instance + * @param purge If `true`, will also purge the `Keyboard` instance itself from the cache. * If `false`, only forgets the metadata (stubs). */ - forgetKeyboard(keyboard: string | JSKeyboard, purge: boolean = false) { - const id: string = (keyboard instanceof JSKeyboard) ? keyboard.id : toPrefixedKeyboardId(keyboard); + forgetKeyboard(keyboard: string | Keyboard, purge: boolean = false) { + const id: string = (keyboard instanceof Keyboard) ? keyboard.id : toPrefixedKeyboardId(keyboard); if(this.stubSetTable[id]) { delete this.stubSetTable[id]; diff --git a/web/src/engine/src/keyboard/index.ts b/web/src/engine/src/keyboard/index.ts index c292f11266..e858a20f52 100644 --- a/web/src/engine/src/keyboard/index.ts +++ b/web/src/engine/src/keyboard/index.ts @@ -1,10 +1,11 @@ export { ActiveKeyBase, ActiveKey, ActiveSubKey, ActiveRow, ActiveLayer, ActiveLayout } from "./keyboards/activeLayout.js"; export { ButtonClass, ButtonClasses, LayoutLayer, LayoutFormFactor, LayoutRow, LayoutKey, LayoutSubKey, Layouts } from "./keyboards/defaultLayouts.js"; export { JSKeyboard, LayoutState } from "./keyboards/jsKeyboard.js"; +export { Keyboard } from './keyboards/keyboard.js'; export { KeyboardMinimalInterface } from './keyboards/keyboardMinimalInterface.js'; export { KMXKeyboard } from './keyboards/kmxKeyboard.js'; export { KeyboardHarness, KeyboardKeymanGlobal, MinimalCodesInterface, MinimalKeymanGlobal } from "./keyboards/keyboardHarness.js"; -export { NotifyEventCode, Keyboard, KeyboardLoaderBase } from "./keyboards/keyboardLoaderBase.js"; +export { NotifyEventCode, KeyboardLoaderBase } from "./keyboards/keyboardLoaderBase.js"; export { KeyboardLoadErrorBuilder, KeyboardMissingError, KeyboardScriptError, KeyboardDownloadError, InvalidKeyboardError } from './keyboards/keyboardLoadError.js' export { BeepHandler, EventMap, KeyboardProcessor } from "./keyboards/keyboardProcessor.js"; export { diff --git a/web/src/engine/src/keyboard/keyEvent.ts b/web/src/engine/src/keyboard/keyEvent.ts index c261f0f972..fb5830e459 100644 --- a/web/src/engine/src/keyboard/keyEvent.ts +++ b/web/src/engine/src/keyboard/keyEvent.ts @@ -11,7 +11,7 @@ import { type DeviceSpec } from "keyman/common/web-utils"; import { Codes } from './codes.js'; import { DefaultOutputRules } from "./defaultOutputRules.js"; import { ActiveKeyBase } from './keyboards/activeLayout.js'; -import { type Keyboard } from "./keyboards/keyboardLoaderBase.js"; +import { type Keyboard } from "./keyboards/keyboard.js"; // Represents a probability distribution over a keyboard's keys. // Defined here to avoid compilation issues. diff --git a/web/src/engine/src/keyboard/keyboards/jsKeyboard.ts b/web/src/engine/src/keyboard/keyboards/jsKeyboard.ts index 5fd6e06530..629f7422bd 100644 --- a/web/src/engine/src/keyboard/keyboards/jsKeyboard.ts +++ b/web/src/engine/src/keyboard/keyboards/jsKeyboard.ts @@ -15,6 +15,7 @@ type TouchLayoutSpec = TouchLayout.TouchLayoutPlatform & { isDefault?: boolean}; import { Version, DeviceSpec } from "keyman/common/web-utils"; import { StateKeyMap } from "./stateKeyMap.js"; import { NotifyEventCode } from './keyboardLoaderBase.js'; +import { Keyboard } from './keyboard.js'; /** * Stores preprocessed properties of a keyboard for quick retrieval later. @@ -49,7 +50,7 @@ type KmwKeyboardObject = KeyboardObject & { * and keyboard-centered functionality in an object-oriented way without modifying the * wrapped keyboard itself. */ -export class JSKeyboard { +export class JSKeyboard extends Keyboard { public static DEFAULT_SCRIPT_OBJECT: KmwKeyboardObject = { 'gs': function(textStore: TextStore, keystroke: KeyEvent) { return false; }, // no matching rules; rely on defaultRuleOutput entirely 'KI': '', // The currently-existing default keyboard ID; we already have checks that focus against this. @@ -68,6 +69,7 @@ export class JSKeyboard { private layoutStates: {[layout: string]: LayoutState}; constructor(keyboardScript: any) { + super(); if(keyboardScript) { this.scriptObject = keyboardScript; } else { diff --git a/web/src/engine/src/keyboard/keyboards/keyboard.ts b/web/src/engine/src/keyboard/keyboards/keyboard.ts new file mode 100644 index 0000000000..8b1d5ebe30 --- /dev/null +++ b/web/src/engine/src/keyboard/keyboards/keyboard.ts @@ -0,0 +1,89 @@ +/* + * Keyman is copyright (C) SIL Global. MIT License. + */ +import { DeviceSpec } from 'keyman/common/web-utils'; +import { ActiveKey, ActiveLayout, ActiveSubKey } from './activeLayout.js'; +import { StateKeyMap } from './stateKeyMap.js'; +import { KeyEvent } from '../keyEvent.js'; +import { TextStore } from '../textStore.js'; +import { NotifyEventCode } from './keyboardLoaderBase.js'; + +/** + * Abstract base class for Keyman keyboards, providing common interface + * for JSKeyboard and KMXKeyboard. + */ +export abstract class Keyboard { + /** + * Unique identifier for the keyboard. + */ + abstract get id(): string; + + /** + * Gets the name of the keyboard. + */ + abstract get name(): string; + + /** + * Version string of the keyboard. + */ + abstract get version(): string; + + /** + * Indicates whether the keyboard uses mnemonic layout. + */ + abstract get isMnemonic(): boolean; + + /** + * Indicates whether the keyboard is chiral (distinguishes left/right modifiers). + */ + abstract get isChiral(): boolean; + + /** + * Indicates whether the keyboard emulates AltGr. + */ + abstract get emulatesAltGr(): boolean; + + /** + * Indicates whether the keyboard is right-to-left. + */ + abstract get isRTL(): boolean; + + /** + * true if this keyboard uses a (legacy) pick list (Chinese, Japanese, Korean, etc.) + */ + abstract get isCJK(): boolean; + + /** + * CSS styling for the on-screen keyboard. + */ + abstract get oskStyling(): string; + + /** + * Returns an ActiveLayout object representing the keyboard's layout for this form factor. + * May return null if a custom desktop "help" OSK is defined. + * @param formFactor The desired form factor for the layout. + */ + abstract layout(formFactor: DeviceSpec.FormFactor): ActiveLayout; + + /** + * Indicates whether the keyboard's desktop layout should be used for the specified device. + * @param device The device specification to check. + */ + abstract usesDesktopLayoutOnDevice(device: DeviceSpec): boolean; + + /** + * Notifies keyboard of keystroke or other event. + * @param eventCode Event code (16-18: Shift, Control or Alt), or 0 for focus. + * @param textStore Text store. + * @param data 1 for KeyDown or FocusReceived, 0 for KeyUp or FocusLost. + */ + abstract notify(eventCode: NotifyEventCode, textStore: TextStore, data: number): void; + + /** + * Constructs a KeyEvent from an ActiveKey or ActiveSubKey. + * @param key The key to construct the event for. + * @param device The device specification. + * @param stateKeys The current state of modifier keys. + */ + abstract constructKeyEvent(key: ActiveKey | ActiveSubKey, device: DeviceSpec, stateKeys: StateKeyMap): KeyEvent; +} \ No newline at end of file diff --git a/web/src/engine/src/keyboard/keyboards/keyboardHarness.ts b/web/src/engine/src/keyboard/keyboards/keyboardHarness.ts index c952dff550..77f34354fa 100644 --- a/web/src/engine/src/keyboard/keyboards/keyboardHarness.ts +++ b/web/src/engine/src/keyboard/keyboards/keyboardHarness.ts @@ -1,6 +1,7 @@ import { JSKeyboard } from "./jsKeyboard.js"; import { Codes } from "../codes.js"; import { DeviceSpec } from 'keyman/common/web-utils'; +import { Keyboard } from './keyboard.js'; /** * Defines members of the top-level `keyman` global object necessary to guarantee @@ -64,7 +65,7 @@ export class KeyboardHarness { /** * This field serves as the receptacle for a successfully-loaded Keyboard. */ - public loadedKeyboard: JSKeyboard = null; + public loadedKeyboard: Keyboard = null; /** * Keyman keyboards register themselves into the Keyman Engine for Web by directly @@ -85,6 +86,7 @@ export class KeyboardHarness { throw new Error("Unexpected state: the most-recently loaded keyboard field was not properly reset."); } this.loadedKeyboard = new JSKeyboard(scriptObject); + // TODO-web-core: do we have to do something similar for KMX keyboards? } // Is evaluated on script-load for some keyboards using variable stores. diff --git a/web/src/engine/src/keyboard/keyboards/keyboardLoaderBase.ts b/web/src/engine/src/keyboard/keyboards/keyboardLoaderBase.ts index 07c0788220..d5cdb436f7 100644 --- a/web/src/engine/src/keyboard/keyboards/keyboardLoaderBase.ts +++ b/web/src/engine/src/keyboard/keyboards/keyboardLoaderBase.ts @@ -1,10 +1,10 @@ import { KM_Core, KM_CORE_STATUS } from 'keyman/engine/core-adapter'; -import { JSKeyboard } from "./jsKeyboard.js"; import { KMXKeyboard } from './kmxKeyboard.js'; import { KeyboardHarness } from "./keyboardHarness.js"; import { KeyboardProperties } from "./keyboardProperties.js"; import { KeyboardLoadErrorBuilder, StubBasedErrorBuilder, UriBasedErrorBuilder } from './keyboardLoadError.js'; import { Codes } from '../codes.js'; +import { Keyboard } from './keyboard.js'; export enum NotifyEventCode { FocusEvent = 0, @@ -14,7 +14,6 @@ export enum NotifyEventCode { }; export type KeyboardStub = KeyboardProperties & { filename: string }; -export type Keyboard = JSKeyboard | KMXKeyboard; export abstract class KeyboardLoaderBase { private _harness: KeyboardHarness; @@ -63,7 +62,7 @@ export abstract class KeyboardLoaderBase { const name = this.extractIdFromUrl(uri); const result = KM_Core.instance.keyboard_load_from_blob(name, byteArray); if (result.status == KM_CORE_STATUS.OK) { - return new KMXKeyboard(result.object); + return new KMXKeyboard(name, result.object); } throw errorBuilder.invalidKeyboard(new Error(`Loading KMX keyboard from ${uri} failed with status ${result.status}`)); } @@ -86,5 +85,5 @@ export abstract class KeyboardLoaderBase { protected abstract loadKeyboardBlob(uri: string, errorBuilder: KeyboardLoadErrorBuilder): Promise; - protected abstract loadKeyboardFromScript(scriptSrc: string, errorBuilder: KeyboardLoadErrorBuilder): Promise; + protected abstract loadKeyboardFromScript(scriptSrc: string, errorBuilder: KeyboardLoadErrorBuilder): Promise; } \ No newline at end of file diff --git a/web/src/engine/src/keyboard/keyboards/keyboardMinimalInterface.ts b/web/src/engine/src/keyboard/keyboards/keyboardMinimalInterface.ts index 7960b0708b..a130160527 100644 --- a/web/src/engine/src/keyboard/keyboards/keyboardMinimalInterface.ts +++ b/web/src/engine/src/keyboard/keyboards/keyboardMinimalInterface.ts @@ -1,5 +1,5 @@ import { VariableStoreSerializer } from '../variableStore.js'; -import { Keyboard } from './keyboardLoaderBase.js'; +import { Keyboard } from './keyboard.js'; export interface KeyboardMinimalInterface { activeKeyboard: Keyboard; diff --git a/web/src/engine/src/keyboard/keyboards/keyboardProcessor.ts b/web/src/engine/src/keyboard/keyboards/keyboardProcessor.ts index 6b906fbfec..3d29673c80 100644 --- a/web/src/engine/src/keyboard/keyboards/keyboardProcessor.ts +++ b/web/src/engine/src/keyboard/keyboards/keyboardProcessor.ts @@ -5,7 +5,7 @@ import { EventEmitter } from 'eventemitter3'; import { DeviceSpec } from 'keyman/common/web-utils'; import { KeyEvent } from '../keyEvent.js'; import { type MutableSystemStore } from "../systemStore.js"; -import { Keyboard } from './keyboardLoaderBase.js'; +import { Keyboard } from './keyboard.js'; import { KeyboardMinimalInterface } from './keyboardMinimalInterface.js'; import { ProcessorAction } from './processorAction.js'; import { StateKeyMap } from './stateKeyMap.js'; diff --git a/web/src/engine/src/keyboard/keyboards/kmxKeyboard.ts b/web/src/engine/src/keyboard/keyboards/kmxKeyboard.ts index 76d1e844ff..46a9a82ea7 100644 --- a/web/src/engine/src/keyboard/keyboards/kmxKeyboard.ts +++ b/web/src/engine/src/keyboard/keyboards/kmxKeyboard.ts @@ -8,14 +8,16 @@ import { StateKeyMap } from './stateKeyMap.js'; import { KeyEvent } from '../keyEvent.js'; import { TextStore } from '../textStore.js'; import { NotifyEventCode } from './keyboardLoaderBase.js'; +import { Keyboard } from './keyboard.js'; /** * Acts as a wrapper class for KMX(+) Keyman keyboards */ -export class KMXKeyboard { - private _state: km_core_state; +export class KMXKeyboard extends Keyboard { + private _state: km_core_state | null = null; - public constructor(private _keyboard: km_core_keyboard) { + public constructor(private _name: string, private _keyboard: km_core_keyboard) { + super(); const environment_opts = [ { @@ -77,6 +79,10 @@ export class KMXKeyboard { return id; } + public get name(): string { + return this._name; + } + public get keyboard(): km_core_keyboard { return this._keyboard; } @@ -124,6 +130,15 @@ export class KMXKeyboard { return false; } + /** + * Returns always false because (legacy) pick lists (Chinese, Japanese, Korean, etc.) + * are not supported when using KMX keyboards. + */ + public get isCJK(): boolean { + // always return false + return false; + } + /** * Returns an ActiveLayout object representing the keyboard's layout for this * form factor. May return null if a custom desktop "help" OSK is defined, diff --git a/web/src/engine/src/keyboard/keyboards/loaders/domKeyboardLoader.ts b/web/src/engine/src/keyboard/keyboards/loaders/domKeyboardLoader.ts index 7f1b0dcfdd..6b961ac831 100644 --- a/web/src/engine/src/keyboard/keyboards/loaders/domKeyboardLoader.ts +++ b/web/src/engine/src/keyboard/keyboards/loaders/domKeyboardLoader.ts @@ -2,7 +2,7 @@ /// -import { JSKeyboard } from '../jsKeyboard.js'; +import { Keyboard } from '../keyboard.js'; import { KeyboardHarness, MinimalKeymanGlobal } from '../keyboardHarness.js'; import { KeyboardLoaderBase } from '../keyboardLoaderBase.js'; import { KeyboardLoadErrorBuilder } from '../keyboardLoadError.js'; @@ -54,7 +54,7 @@ export class DOMKeyboardLoader extends KeyboardLoaderBase { return new Uint8Array(buffer); } - protected async loadKeyboardFromScript(script: string, errorBuilder: KeyboardLoadErrorBuilder): Promise { + protected async loadKeyboardFromScript(script: string, errorBuilder: KeyboardLoadErrorBuilder): Promise { try { this.evalScriptInContext(script, this.harness._jsGlobal); } catch (e) { diff --git a/web/src/engine/src/main/headless/inputProcessor.ts b/web/src/engine/src/main/headless/inputProcessor.ts index b6edf65da0..983dc1bdcf 100644 --- a/web/src/engine/src/main/headless/inputProcessor.ts +++ b/web/src/engine/src/main/headless/inputProcessor.ts @@ -9,14 +9,14 @@ import { CoreKeyboardProcessor } from 'keyman/engine/core-processor'; import { Codes, - JSKeyboard, // TODO-web-core: huh? why is this here + JSKeyboard, // required to be able to distinguish between JS and Core kbd processor + Keyboard, KeyboardMinimalInterface, SyntheticTextStore, TextStore, ProcessorAction, SystemStoreIDs, type Alternate, - type Keyboard, type KeyEvent, KeyboardProcessor, VariableStoreSerializer diff --git a/web/src/engine/src/main/keymanEngineBase.ts b/web/src/engine/src/main/keymanEngineBase.ts index d862162328..bdb43e05ea 100644 --- a/web/src/engine/src/main/keymanEngineBase.ts +++ b/web/src/engine/src/main/keymanEngineBase.ts @@ -3,7 +3,7 @@ import { ProcessorInitOptions } from 'keyman/engine/js-processor'; import { DOMKeyboardLoader } from "keyman/engine/keyboard"; import { WorkerFactory } from "@keymanapp/lexical-model-layer/web" import { InputProcessor } from './headless/inputProcessor.js'; -import { OSKView, JSKeyboardData } from "keyman/engine/osk"; +import { OSKView, KeyboardData } from "keyman/engine/osk"; import { KeyboardRequisitioner, ModelCache, toUnprefixedKeyboardId, DOMCloudRequester } from "keyman/engine/keyboard-storage"; import { ModelSpec, PredictionContext } from "keyman/engine/interfaces"; @@ -386,7 +386,7 @@ export class KeymanEngineBase< this.core.keyboardProcessor.contextDevice = value?.targetDevice ?? this.config.softDevice; if(value) { // Don't build an OSK if no keyboard is available yet; avoid the extra flash. - if (this.contextManager.activeKeyboard && this.contextManager.activeKeyboard instanceof JSKeyboardData) { // TODO-embed-osk-in-kmx: add support for OSK for KMX keyboards + if (this.contextManager.activeKeyboard && this.contextManager.activeKeyboard instanceof KeyboardData) { // TODO-embed-osk-in-kmx: add support for OSK for KMX keyboards value.activeKeyboard = this.contextManager.activeKeyboard; } value.on('keyevent', this.keyEventListener); diff --git a/web/src/engine/src/osk/components/titleBar.ts b/web/src/engine/src/osk/components/titleBar.ts index f7b76bf343..1c262dc57e 100644 --- a/web/src/engine/src/osk/components/titleBar.ts +++ b/web/src/engine/src/osk/components/titleBar.ts @@ -1,6 +1,6 @@ import { EventEmitter } from 'eventemitter3'; -import { JSKeyboard } from 'keyman/engine/keyboard'; +import { Keyboard } from 'keyman/engine/keyboard'; import { OSKViewComponent } from './oskViewComponent.interface.js'; import { ParsedLengthStyle } from '../lengthStyle.js'; @@ -102,7 +102,7 @@ export class TitleBar extends EventEmitter implements OSKVie this._caption.innerHTML = str; } - public setTitleFromKeyboard(keyboard: JSKeyboard) { + public setTitleFromKeyboard(keyboard: Keyboard) { const title = "" + keyboard?.name + ''; // I1972 // I2186 this._caption.innerHTML = title; } diff --git a/web/src/engine/src/osk/index.ts b/web/src/engine/src/osk/index.ts index 292ea9938c..cfb6f21c2c 100644 --- a/web/src/engine/src/osk/index.ts +++ b/web/src/engine/src/osk/index.ts @@ -2,7 +2,7 @@ export { DeviceSpec } from 'keyman/common/web-utils'; export { Codes, JSKeyboard, KeyboardProperties, SpacebarText } from 'keyman/engine/keyboard'; -export { OSKView, JSKeyboardData } from './views/oskView.js'; +export { OSKView, KeyboardData } from './views/oskView.js'; export { FloatingOSKView, FloatingOSKViewConfiguration } from './views/floatingOskView.js'; export { AnchoredOSKView } from './views/anchoredOskView.js'; export { InlinedOSKView } from './views/inlinedOskView.js'; diff --git a/web/src/engine/src/osk/views/oskView.ts b/web/src/engine/src/osk/views/oskView.ts index 85804452f8..ba898caf0f 100644 --- a/web/src/engine/src/osk/views/oskView.ts +++ b/web/src/engine/src/osk/views/oskView.ts @@ -14,6 +14,7 @@ import { DeviceSpec, ManagedPromise } from 'keyman/common/web-utils'; import { Codes, JSKeyboard, + Keyboard, KeyboardProperties, type MinimalCodesInterface, type MutableSystemStore, type SystemStoreMutationHandler, @@ -58,8 +59,8 @@ export interface LegacyOSKEventMap { }): void; } -export class JSKeyboardData { - keyboard: JSKeyboard; +export class KeyboardData { + keyboard: Keyboard; metadata: KeyboardProperties; }; @@ -172,7 +173,7 @@ export abstract class OSKView private _boxBaseTouchStart: (e: TouchEvent) => boolean; private _boxBaseTouchEventCancel: (e: TouchEvent) => boolean; - private keyboardData: JSKeyboardData; + private keyboardData: KeyboardData; /** * Provides the current parameterization for timings and distances used by @@ -553,11 +554,11 @@ export abstract class OSKView } } - public get activeKeyboard(): JSKeyboardData { + public get activeKeyboard(): KeyboardData { return this.keyboardData; } - public set activeKeyboard(keyboardData: JSKeyboardData) { + public set activeKeyboard(keyboardData: KeyboardData) { this.keyboardData = keyboardData; this.loadActiveKeyboard(); @@ -765,7 +766,7 @@ export abstract class OSKView // Create new ones for the new, incoming kbd. this.kbdStyleSheetManager = new StylesheetManager(this._Box, this.config.doCacheBusting || false); - const kbdView = this.keyboardView = this._GenerateKeyboardView(this.keyboardData?.keyboard, this.keyboardData?.metadata); + const kbdView = this.keyboardView = this._GenerateKeyboardView(this.keyboardData?.keyboard as JSKeyboard, this.keyboardData?.metadata); // Perform the replacement. this._Box.replaceChild(kbdView.element, oldKbd.element); diff --git a/web/src/engine/src/osk/visualKeyboard.ts b/web/src/engine/src/osk/visualKeyboard.ts index 6cdaf3ad64..3faa214a4b 100644 --- a/web/src/engine/src/osk/visualKeyboard.ts +++ b/web/src/engine/src/osk/visualKeyboard.ts @@ -1356,7 +1356,7 @@ export class VisualKeyboard extends EventEmitter implements KeyboardVi * @return {Object} DIV object with filled keyboard layer content */ static buildDocumentationKeyboard( - PKbd: JSKeyboard, + PKbd: JSKeyboard, // TODO-web-core: do we have to do anything for KMX keyboards? kbdProperties: KeyboardProperties, pathConfig: OSKResourcePathConfiguration, argFormFactor: DeviceSpec.FormFactor, diff --git a/web/src/test/auto/dom/kbdLoader.ts b/web/src/test/auto/dom/kbdLoader.ts index 3e43ab7ff7..cf55584262 100644 --- a/web/src/test/auto/dom/kbdLoader.ts +++ b/web/src/test/auto/dom/kbdLoader.ts @@ -1,9 +1,7 @@ import { - DOMKeyboardLoader -} from 'keyman/engine/keyboard'; - -import { + DOMKeyboardLoader, JSKeyboard, + Keyboard, KeyboardProperties, MinimalKeymanGlobal } from 'keyman/engine/keyboard'; @@ -18,13 +16,13 @@ export function loadKeyboardFromPath(path: string) { } export type KeyboardMap = { - [key: string]: KeyboardInfoPair & { keyboard: JSKeyboard } + [key: string]: KeyboardInfoPair & { keyboard: Keyboard } }; export function loadKeyboardsFromStubs(apiStubs: any, baseDir: string) { baseDir = baseDir || './'; const keyboards: KeyboardMap = {}; - let priorPromise: Promise = Promise.resolve(); + let priorPromise: Promise = Promise.resolve(); for(const stub of apiStubs) { // We are keeping this strictly sequential because we don't have sandboxed // loading yet; lack of sandboxing means that all loading keyboards compete diff --git a/web/src/test/auto/headless/engine/core-processor/coreKeyboardProcessor.tests.ts b/web/src/test/auto/headless/engine/core-processor/coreKeyboardProcessor.tests.ts index 7c4e3690fe..f3ca77f8fc 100644 --- a/web/src/test/auto/headless/engine/core-processor/coreKeyboardProcessor.tests.ts +++ b/web/src/test/auto/headless/engine/core-processor/coreKeyboardProcessor.tests.ts @@ -520,7 +520,7 @@ describe('CoreKeyboardProcessor', function () { keyEvent.source = { type: eventType }; const coreKeyboard = loadKeyboard('/common/test/resources/keyboards/test_8568_deadkeys.kmx'); - const kmxKeyboard = new KMXKeyboard(coreKeyboard); + const kmxKeyboard = new KMXKeyboard('test_8568_deadkeys', coreKeyboard); sandbox.replaceGetter(coreProcessor, 'activeKeyboard', () => { return kmxKeyboard; }); // We return a non-ok value just so that we can return early from // processKeyStroke() @@ -546,7 +546,7 @@ describe('CoreKeyboardProcessor', function () { context = KM_Core.instance.state_context(state); sandbox = sinon.createSandbox(); const coreKeyboard = loadKeyboard('/common/test/resources/keyboards/test_8568_deadkeys.kmx'); - coreProcessor.activeKeyboard = new KMXKeyboard(coreKeyboard); + coreProcessor.activeKeyboard = new KMXKeyboard('test_8568_deadkeys', coreKeyboard); }); afterEach(() => { diff --git a/web/src/test/auto/headless/engine/js-processor/specialized-backspace.tests.ts b/web/src/test/auto/headless/engine/js-processor/specialized-backspace.tests.ts index 581b1810e0..dca0ca37f8 100644 --- a/web/src/test/auto/headless/engine/js-processor/specialized-backspace.tests.ts +++ b/web/src/test/auto/headless/engine/js-processor/specialized-backspace.tests.ts @@ -4,7 +4,7 @@ import { createRequire } from 'module'; const require = createRequire(import.meta.url); import { DeviceSpec, KMWString } from 'keyman/common/web-utils'; -import { Codes, DefaultOutputRules, JSKeyboard, KeyEvent, MinimalKeymanGlobal, SyntheticTextStore } from 'keyman/engine/keyboard'; +import { Codes, DefaultOutputRules, KeyEvent, MinimalKeymanGlobal, SyntheticTextStore } from 'keyman/engine/keyboard'; import { JSKeyboardInterface, JSKeyboardProcessor } from 'keyman/engine/js-processor'; import { NodeKeyboardLoader } from 'keyman/test/resources'; import { ModifierKeyConstants } from '@keymanapp/common-types'; @@ -103,7 +103,7 @@ describe('Engine - specialized backspace handling', function() { // This part provides extra assurance that the keyboard properly loaded. assert.equal(keyboard.id, "Keyboard_sil_ipa"); - harness.activeKeyboard = keyboard as JSKeyboard; + harness.activeKeyboard = keyboard; ipaWithHarness = harness; // -------------- @@ -117,7 +117,7 @@ describe('Engine - specialized backspace handling', function() { // This part provides extra assurance that the keyboard properly loaded. assert.equal(keyboard.id, "Keyboard_khmer_angkor"); - harness.activeKeyboard = keyboard as JSKeyboard; + harness.activeKeyboard = keyboard; angkorWithHarness = harness; // -------------- diff --git a/web/src/test/auto/resources/loader/nodeKeyboardLoader.ts b/web/src/test/auto/resources/loader/nodeKeyboardLoader.ts index ee737eb206..c9079e1165 100644 --- a/web/src/test/auto/resources/loader/nodeKeyboardLoader.ts +++ b/web/src/test/auto/resources/loader/nodeKeyboardLoader.ts @@ -3,7 +3,7 @@ import { readFile } from 'node:fs/promises'; import { globalObject } from 'keyman/common/web-utils'; -import { JSKeyboard, KeyboardHarness, MinimalKeymanGlobal, KeyboardLoaderBase, KeyboardLoadErrorBuilder } from 'keyman/engine/keyboard'; +import { Keyboard, KeyboardHarness, MinimalKeymanGlobal, KeyboardLoaderBase, KeyboardLoadErrorBuilder } from 'keyman/engine/keyboard'; export class NodeKeyboardLoader extends KeyboardLoaderBase { constructor() @@ -38,7 +38,7 @@ export class NodeKeyboardLoader extends KeyboardLoaderBase { return Uint8Array.from(buffer); } - protected async loadKeyboardFromScript(scriptSrc: string, errorBuilder: KeyboardLoadErrorBuilder): Promise { + protected async loadKeyboardFromScript(scriptSrc: string, errorBuilder: KeyboardLoadErrorBuilder): Promise { let script; try { script = new vm.Script(scriptSrc);