diff --git a/web/src/app/browser/src/keymanEngine.ts b/web/src/app/browser/src/keymanEngine.ts index 75f0f9bfc7..d0d51837da 100644 --- a/web/src/app/browser/src/keymanEngine.ts +++ b/web/src/app/browser/src/keymanEngine.ts @@ -8,7 +8,7 @@ import { VisualKeyboard } from 'keyman/engine/osk'; import { ErrorStub, KeyboardStub, CloudQueryResult, toPrefixedKeyboardId as prefixed } from 'keyman/engine/package-cache'; -import { DeviceSpec, Keyboard, ProcessorInitOptions, extendString } from "@keymanapp/keyboard-processor"; +import { DeviceSpec, Keyboard, extendString } from "@keymanapp/keyboard-processor"; import * as views from './viewsAnchorpoint.js'; import { BrowserConfiguration, BrowserInitOptionDefaults, BrowserInitOptionSpec } from './configuration.js'; @@ -48,7 +48,15 @@ export default class KeymanEngine extends KeymanEngineBase this.legacyAPIEvents)); + super(worker, config, new ContextManager(config, () => this.legacyAPIEvents), (engine: KeymanEngine) => { + return { + // The `engine` parameter cannot be supplied with the constructing instance before calling + // `super`, hence the 'fun' rigging to supply it _from_ `super` via this closure. + keyboardInterface: new KeyboardInterface(window, engine), + defaultOutputRules: new DefaultBrowserRules(engine.contextManager) + }; + }); + this._util = new UtilApiEndpoint(config); this.beepHandler = new BeepHandler(this.core.keyboardInterface); this.core.keyboardProcessor.beepHandler = () => this.beepHandler.beep(this.contextManager.activeTarget); @@ -111,15 +119,6 @@ export default class KeymanEngine extends KeymanEngineBase) { let deviceDetector = new DeviceDetector(); let device = deviceDetector.detect(); diff --git a/web/src/app/webview/src/keymanEngine.ts b/web/src/app/webview/src/keymanEngine.ts index 93120af86c..1cc7f08248 100644 --- a/web/src/app/webview/src/keymanEngine.ts +++ b/web/src/app/webview/src/keymanEngine.ts @@ -1,5 +1,5 @@ -import { DeviceSpec } from '@keymanapp/keyboard-processor' -import { KeymanEngine as KeymanEngineBase } from 'keyman/engine/main'; +import { DefaultRules, DeviceSpec } from '@keymanapp/keyboard-processor' +import { KeymanEngine as KeymanEngineBase, KeyboardInterface } from 'keyman/engine/main'; import { AnchoredOSKView, ViewConfiguration, StaticActivator } from 'keyman/engine/osk'; import { getAbsoluteX, getAbsoluteY } from 'keyman/engine/dom-utils'; import { type KeyboardStub, toPrefixedKeyboardId, toUnprefixedKeyboardId } from 'keyman/engine/package-cache'; @@ -25,7 +25,14 @@ export default class KeymanEngine extends KeymanEngineBase { + return { + // The `engine` parameter cannot be supplied with the constructing instance before calling + // `super`, hence the 'fun' rigging to supply it _from_ `super` via this closure. + keyboardInterface: new KeyboardInterface(window, engine, config.stubNamespacer), + defaultOutputRules: new DefaultRules() + }; + }); this.hardKeyboard = new PassthroughKeyboard(config.hardDevice); } diff --git a/web/src/engine/main/src/keymanEngine.ts b/web/src/engine/main/src/keymanEngine.ts index 76f61055f9..b34966897c 100644 --- a/web/src/engine/main/src/keymanEngine.ts +++ b/web/src/engine/main/src/keymanEngine.ts @@ -1,8 +1,8 @@ -import { DefaultRules, type Keyboard, KeyboardKeymanGlobal, ProcessorInitOptions, OutputTarget } from "@keymanapp/keyboard-processor"; +import { type Keyboard, KeyboardKeymanGlobal, ProcessorInitOptions } from "@keymanapp/keyboard-processor"; import { DOMKeyboardLoader as KeyboardLoader } from "@keymanapp/keyboard-processor/dom-keyboard-loader"; import { InputProcessor, PredictionContext } from "@keymanapp/input-processor"; import { OSKView } from "keyman/engine/osk"; -import { KeyboardRequisitioner, type KeyboardStub, ModelCache, ModelSpec } from "keyman/engine/package-cache"; +import { KeyboardRequisitioner, ModelCache, ModelSpec } from "keyman/engine/package-cache"; import { EngineConfiguration, InitOptionSpec } from "./engineConfiguration.js"; import KeyboardInterface from "./keyboardInterface.js"; @@ -14,6 +14,19 @@ import { EventNames, EventListener, LegacyEventEmitter } from "keyman/engine/eve import DOMCloudRequester from "keyman/engine/package-cache/dom-requester"; import KEYMAN_VERSION from "@keymanapp/keyman-version"; +// From https://stackoverflow.com/a/69328045 +type WithRequired = T & { [P in K]-?: T[P] }; +// Sets two parts non-optional at this level, while they were at lower levels. +type ProcessorConfiguration = WithRequired, 'defaultOutputRules'>; + +function determineBaseLayout(): string { + if(typeof(window['KeymanWeb_BaseLayout']) !== 'undefined') { + return window['KeymanWeb_BaseLayout']; + } else { + return 'us'; + } +} + export default class KeymanEngine< Configuration extends EngineConfiguration, ContextManager extends ContextManagerBase, @@ -73,26 +86,6 @@ export default class KeymanEngine< // processing - silent failures are far harder to diagnose. }; - // Should be overwritten as needed by engine subclasses; `browser` should set its DefaultOutput subclass in place. - protected processorConfiguration(): ProcessorInitOptions { - // I732 START - Support for European underlying keyboards #1 - let baseLayout: string; - if(typeof(window['KeymanWeb_BaseLayout']) !== 'undefined') { - baseLayout = window['KeymanWeb_BaseLayout']; - } else { - baseLayout = 'us'; - } - - return { - keyboardInterface: this.interface || - new KeyboardInterface(window, this, this.config.stubNamespacer), - baseLayout: baseLayout, - defaultOutputRules: new DefaultRules() - }; - }; - - // - /** * @param worker A configured WebWorker to serve as the predictive-text engine's main thread. * Available in the following variants: @@ -100,12 +93,21 @@ export default class KeymanEngine< * - non-sourcemapped + minified (release) * @param config * @param contextManager + * @param processorConfigInitializer A one-time use closure used to initialize certain critical components reliant + * upon the class instance, configured by the derived class, but needed during + * the superclass constructor. */ - constructor(worker: Worker, config: Configuration, contextManager: ContextManager) { + constructor( + worker: Worker, + config: Configuration, + contextManager: ContextManager, + processorConfigInitializer: (engine: KeymanEngine) => ProcessorConfiguration + ) { this.config = config; this.contextManager = contextManager; - const processorConfiguration = this.processorConfiguration(); + const processorConfiguration = processorConfigInitializer(this); + processorConfiguration.baseLayout = determineBaseLayout(); this.interface = processorConfiguration.keyboardInterface as KeyboardInterface; this.core = new InputProcessor(config.hostDevice, worker, processorConfiguration);