chore(web): Merge branch 'feature-esmodule-web-engine' into chore/web/centralize-kmw-tsconfigs

This commit is contained in:
Joshua A. Horton 2023-06-19 14:03:20 +07:00
commit f2e3effa4c
27 changed files with 174 additions and 174 deletions

View file

@ -37,7 +37,7 @@ export interface ProcessorInitOptions {
}
interface EventMap {
statekeyChange: (stateKeys: typeof KeyboardProcessor.prototype.stateKeys) => void;
statekeychange: (stateKeys: typeof KeyboardProcessor.prototype.stateKeys) => void;
}
export default class KeyboardProcessor extends EventEmitter<EventMap> {
@ -287,7 +287,7 @@ export default class KeyboardProcessor extends EventEmitter<EventMap> {
}
if(stateMutation) {
this.emit('statekeyChange', this.stateKeys);
this.emit('statekeychange', this.stateKeys);
}
}

View file

@ -389,7 +389,7 @@ export default class HardwareEventKeyboard extends HardKeyboard {
}
// Is synchronous.
this.emit('keyEvent', Levent, (ruleBehavior, error) => {
this.emit('keyevent', Levent, (ruleBehavior, error) => {
resultCapture.LeventMatched = ruleBehavior && !ruleBehavior.triggerKeyDefault;
if(resultCapture.LeventMatched) {
@ -454,7 +454,7 @@ export default class HardwareEventKeyboard extends HardKeyboard {
// interpretation as well by _not_ evaluating it during this pass.
if(!this.swallowKeypress) {
// is synchronous
this.emit('keyEvent', Levent, (result, error) => {
this.emit('keyevent', Levent, (result, error) => {
resultCapture.preventDefaultKeystroke = !!result;
})
}

View file

@ -6,12 +6,6 @@ import ContextManager from './contextManager.js';
import KeymanEngine from './keymanEngine.js';
export default class KeyboardInterface extends KeyboardInterfaceBase<ContextManager> {
// TBD: allowing it to be set and/or the retrieval mechanism.
// Note that the OSK is constructed notably later, after page load + during full engine init.
// So, the actual instance will not be available at construction-time.
private osk: OSKView; // Or some way to retrieve it.
constructor(
_jsGlobal: any,
engine: KeymanEngine,
@ -20,6 +14,7 @@ export default class KeyboardInterface extends KeyboardInterfaceBase<ContextMana
// Nothing else to do here... quite yet. Things may not stay that way, though.
}
// *** The following are quite useful for website-integrating KMW, but not needed for the embedded form. ***
/**
@ -44,12 +39,12 @@ export default class KeyboardInterface extends KeyboardInterfaceBase<ContextMana
//The following entry points are defined but should not normally be used in a keyboard, as OSK display is no longer determined by the keyboard
hideHelp(): void {
const osk = this.osk;
const osk = this.engine.osk;
osk.startHide(true);
}
showHelp(Px: number, Py: number): void {
const osk = this.osk;
const osk = this.engine.osk;
if(osk instanceof FloatingOSKView) {
osk.presentAtPosition(Px,Py);
@ -59,7 +54,7 @@ export default class KeyboardInterface extends KeyboardInterfaceBase<ContextMana
}
showPinnedHelp(): void {
const osk = this.osk;
const osk = this.engine.osk;
if(osk instanceof FloatingOSKView) {
// An old KMW bug previously auto-unset the affected field when this function was

View file

@ -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';
@ -26,6 +26,7 @@ import { UtilApiEndpoint} from './utilApiEndpoint.js';
import { UIModule } from './uiModuleInterface.js';
import { HotkeyManager } from './hotkeyManager.js';
import { BeepHandler } from './beepHandler.js';
import KeyboardInterface from './keyboardInterface.js';
export default class KeymanEngine extends KeymanEngineBase<BrowserConfiguration, ContextManager, HardwareEventKeyboard> {
touchLanguageMenu?: LanguageMenu;
@ -47,7 +48,15 @@ export default class KeymanEngine extends KeymanEngineBase<BrowserConfiguration,
constructor(worker: Worker, sourceUri: string) {
const config = new BrowserConfiguration(sourceUri); // currently set to perform device auto-detect.
super(worker, config, new ContextManager(config, () => 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);
@ -110,14 +119,6 @@ export default class KeymanEngine extends KeymanEngineBase<BrowserConfiguration,
}
}
protected processorConfiguration(): ProcessorInitOptions {
return {
...super.processorConfiguration(),
// Overrides just this component of the configuration.
defaultOutputRules: new DefaultBrowserRules(this.contextManager)
};
};
async init(options: Required<BrowserInitOptionSpec>) {
let deviceDetector = new DeviceDetector();
let device = deviceDetector.detect();

View file

@ -43,7 +43,7 @@ export class LanguageMenu {
if(sX > spaceBar.offsetLeft && sX < spaceBar.offsetLeft+spaceBar.offsetWidth &&
sY > spaceBar.offsetTop && sY < spaceBar.offsetTop+spaceBar.offsetHeight
) {
this.keyman.osk.emit('showBuild');
this.keyman.osk.emit('showbuild');
}
}
},false);

View file

@ -7,7 +7,7 @@ import { LanguageMenu } from "./languageMenu.js";
export function setupOskListeners(engine: KeymanEngine, osk: OSKView, contextManager: ContextManager) {
const focusAssistant = contextManager.focusAssistant;
osk.on('globeKey', (key, on) => { // K_LOPT
osk.on('globekey', (key, on) => { // K_LOPT
if(on) {
if(osk.hostDevice.touchable) {
engine.touchLanguageMenu = new LanguageMenu(engine);
@ -20,26 +20,26 @@ export function setupOskListeners(engine: KeymanEngine, osk: OSKView, contextMan
}
});
osk.on('hideRequested', (key) => { // K_ROPT
osk.on('hiderequested', (key) => { // K_ROPT
if(osk) {
osk.startHide(true);
contextManager.forgetActiveTarget();
}
});
osk.on('onhide', (hiddenByUser) => {
osk.addEventListener('hide', (params) => {
// If hidden by the UI, be sure to restore the focus
if(hiddenByUser) {
if(params?.HiddenByUser) {
contextManager.activeTarget?.focus();
}
});
osk.on('showBuild', () => {
osk.on('showbuild', () => {
engine.config.alertHost?.alert('KeymanWeb Version ' + KEYMAN_VERSION.VERSION + '<br /><br />'
+'<span style="font-size:0.8em">Copyright &copy; 2007-2023 SIL International</span>');
});
osk.on('dragMove', async (promise) => {
osk.on('dragmove', async (promise) => {
focusAssistant.restoringFocus = true;
await promise;
@ -50,7 +50,7 @@ export function setupOskListeners(engine: KeymanEngine, osk: OSKView, contextMan
focusAssistant.setMaintainingFocus(false);
});
osk.on('resizeMove', async (promise) => {
osk.on('resizemove', async (promise) => {
focusAssistant.restoringFocus = true;
await promise;
@ -60,7 +60,7 @@ export function setupOskListeners(engine: KeymanEngine, osk: OSKView, contextMan
focusAssistant.setMaintainingFocus(false);
});
osk.on('pointerInteraction', async (promise) => {
osk.on('pointerinteraction', async (promise) => {
// On event start
focusAssistant.setMaintainingFocus(true);

View file

@ -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<WebviewConfiguration,
}
}
super(worker, config, new ContextManager(config));
super(worker, config, new ContextManager(config), (engine) => {
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);
}

View file

@ -9,7 +9,7 @@ import { PendingLongpress } from './osk/pendingLongpress.js';
import type KeymanEngine from "./keymanEngine.js";
export function setupEmbeddedListeners(engine: KeymanEngine, osk: OSKView) {
osk.on('globeKey', (key, on) => {
osk.on('globekey', (key, on) => {
if(on) {
if(typeof engine.showKeyboardList == 'function') { // OSKView event: shouldShowLanguageMenu
engine.showKeyboardList(); // Is connected to VisualKeyboard event: globeKey
@ -29,7 +29,7 @@ export function setupEmbeddedListeners(engine: KeymanEngine, osk: OSKView) {
}
});
osk.on('hideRequested', (key) => {
osk.on('hiderequested', (key) => {
if(osk.vkbd) {
osk.vkbd.highlightKey(key, false);
}

View file

@ -40,7 +40,7 @@ export default class PassthroughKeyboard extends HardKeyboard {
const promise = new ManagedPromise<Boolean>();
try {
this.emit('keyEvent', Lkc, (result, error) => {
this.emit('keyevent', Lkc, (result, error) => {
if(error) {
promise.reject(error);
} else {

View file

@ -9,14 +9,14 @@ interface EventMap<BaseEventMap extends Object> {
* EventEmitter being spied upon.
* @param eventName
*/
listenerAdded(eventName: EventNames<BaseEventMap>);
listeneradded(eventName: EventNames<BaseEventMap>);
/**
* Indicates that a listener for the named event has been unregistered from the
* EventEmitter being spied upon.
* @param eventName
*/
listenerRemoved(eventName: EventNames<BaseEventMap>);
listenerremoved(eventName: EventNames<BaseEventMap>);
}
type Emitter<BaseEventMap extends Object> = EventEmitter<BaseEventMap> | LegacyEventEmitter<BaseEventMap>;
@ -30,19 +30,19 @@ export class EmitterListenerSpy<BaseEventMap extends Object> extends EventEmitte
super();
if(emitter instanceof EventEmitter) {
emitter.on = this.listenerRegistrationSpy('listenerAdded', emitter, emitter.on);
emitter.addListener = this.listenerRegistrationSpy('listenerAdded', emitter, emitter.addListener);
emitter.off = this.listenerRegistrationSpy('listenerRemoved', emitter, emitter.off);
emitter.removeListener = this.listenerRegistrationSpy('listenerRemoved', emitter, emitter.off);
emitter.on = this.listenerRegistrationSpy('listeneradded', emitter, emitter.on);
emitter.addListener = this.listenerRegistrationSpy('listeneradded', emitter, emitter.addListener);
emitter.off = this.listenerRegistrationSpy('listenerremoved', emitter, emitter.off);
emitter.removeListener = this.listenerRegistrationSpy('listenerremoved', emitter, emitter.off);
} else {
// TS gets really fussy about how the legacy event typing is a bit more
// restrictive (due to less-restricted event name types in EventEmitter)
// It's not worth the effort to make this 100% perfect at the moment.
//
// @ts-ignore
emitter.addEventListener = this.listenerRegistrationSpy('listenerAdded', emitter, emitter.addEventListener);
emitter.addEventListener = this.listenerRegistrationSpy('listeneradded', emitter, emitter.addEventListener);
// @ts-ignore
emitter.removeEventListener = this.listenerRegistrationSpy('listenerRemoved', emitter, emitter.removeEventListener);
emitter.removeEventListener = this.listenerRegistrationSpy('listenerremoved', emitter, emitter.removeEventListener);
}
}
@ -82,7 +82,7 @@ export class EmitterListenerSpy<BaseEventMap extends Object> extends EventEmitte
// const emitter = new LegacyEventEmitter<TestMap>; // or `new EventEmitter<TestMap>`.
// const emitterSpy = new EmitterListenerSpy<TestMap>(emitter);
// emitterSpy.on('listenerAdded', (eventName) => {
// emitterSpy.on('listeneradded', (eventName) => {
// // eventName = 'c'; // will error; there is no event 'c' in the event map.
// if(eventName == 'a') {
// // stuff

View file

@ -95,7 +95,7 @@ export class LegacyEventEmitter<EventTypes extends LegacyEventMap> {
return this._removeEventListener(event, func);
}
// Separate, in order to prevent `addEventListener` from sending 'listenerRemoved' events with
// Separate, in order to prevent `addEventListener` from sending 'listenerremoved' events with
// EmitterListenerSpy.
private _removeEventListener<T extends EventNames<EventTypes>> (
event: T,

View file

@ -6,7 +6,7 @@ interface EventMap {
/**
* Designed to pass key events off to any consuming modules/libraries.
*/
'keyEvent': (event: KeyEvent, callback?: (result: RuleBehavior, error?: Error) => void) => void
'keyevent': (event: KeyEvent, callback?: (result: RuleBehavior, error?: Error) => void) => void
}
export default class HardKeyboard extends EventEmitter<EventMap> implements KeyEventSourceInterface { }

View file

@ -8,7 +8,7 @@ interface EventMap {
/**
* Designed to pass key events off to any consuming modules/libraries.
*/
'keyEvent': KeyEventHandler;
'keyevent': KeyEventHandler;
}
export default interface KeyEventSourceInterface extends EventEmitter<EventMap> { }

View file

@ -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, K extends keyof T> = T & { [P in K]-?: T[P] };
// Sets two parts non-optional at this level, while they were at lower levels.
type ProcessorConfiguration = WithRequired<WithRequired<ProcessorInitOptions, 'keyboardInterface'>, '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<any>,
@ -73,25 +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,
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:
@ -99,13 +93,23 @@ 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<Configuration, ContextManager, HardKeyboard>) => ProcessorConfiguration
) {
this.config = config;
this.contextManager = contextManager;
this.interface = new KeyboardInterface(window, this, config.stubNamespacer);
this.core = new InputProcessor(config.hostDevice, worker, this.processorConfiguration());
const processorConfiguration = processorConfigInitializer(this);
processorConfiguration.baseLayout = determineBaseLayout();
this.interface = processorConfiguration.keyboardInterface as KeyboardInterface<ContextManager>;
this.core = new InputProcessor(config.hostDevice, worker, processorConfiguration);
this.core.languageProcessor.on('statechange', (state) => {
// The banner controller cannot directly trigger a layout-refresh at this time,
@ -117,7 +121,7 @@ export default class KeymanEngine<
// The OSK does not possess a direct connection to the KeyboardProcessor's state-key
// management object; this event + handler allow us to keep the OSK's related states
// in sync.
this.core.keyboardProcessor.on('statekeyChange', (stateKeys) => {
this.core.keyboardProcessor.on('statekeychange', (stateKeys) => {
this.osk?.vkbd?.updateStateKeys(stateKeys);
})
@ -211,7 +215,7 @@ export default class KeymanEngine<
this.osk?.refreshLayout();
});
kbdCache.on('stubAdded', (stub) => {
kbdCache.on('stubadded', (stub) => {
let eventRaiser = () => {
// The corresponding event is needed in order to update UI modules as new keyboard stubs "come online".
this.legacyAPIEvents.callEvent('keyboardregistered', {
@ -236,7 +240,7 @@ export default class KeymanEngine<
}
});
kbdCache.on('keyboardAdded', (keyboard) => {
kbdCache.on('keyboardadded', (keyboard) => {
let eventRaiser = () => {
// Execute any external (UI) code needed after loading keyboard
this.legacyAPIEvents.callEvent('keyboardloaded', {
@ -251,7 +255,7 @@ export default class KeymanEngine<
}
});
this.keyboardRequisitioner.cache.on('keyboardAdded', (keyboard) => {
this.keyboardRequisitioner.cache.on('keyboardadded', (keyboard) => {
this.legacyAPIEvents.callEvent('keyboardloaded', { keyboardName: keyboard.id });
});
//
@ -272,10 +276,10 @@ export default class KeymanEngine<
protected set hardKeyboard(keyboard: HardKeyboard) {
if(this._hardKeyboard) {
this._hardKeyboard.off('keyEvent', this.keyEventListener);
this._hardKeyboard.off('keyevent', this.keyEventListener);
}
this._hardKeyboard = keyboard;
keyboard.on('keyEvent', this.keyEventListener);
keyboard.on('keyevent', this.keyEventListener);
}
public get osk(): OSKView {
@ -284,13 +288,13 @@ export default class KeymanEngine<
public set osk(value: OSKView) {
if(this._osk) {
this._osk.off('keyEvent', this.keyEventListener);
this._osk.off('keyevent', this.keyEventListener);
this.core.keyboardProcessor.layerStore.handler = this.osk.layerChangeHandler;
}
this._osk = value;
if(value) {
value.activeKeyboard = this.contextManager.activeKeyboard;
value.on('keyEvent', this.keyEventListener);
value.on('keyevent', this.keyEventListener);
this.core.keyboardProcessor.layerStore.handler = value.layerChangeHandler;
}
}

View file

@ -10,7 +10,7 @@ interface EventMap {
/**
* Triggered when the user inputs a special command to show the engine's current version number.
*/
showBuild: () => void;
showbuild: () => void;
}
export default class ResizeBar extends EventEmitter<EventMap, ResizeBar> implements OSKViewComponent {
@ -66,7 +66,7 @@ export default class ResizeBar extends EventEmitter<EventMap, ResizeBar> impleme
// Display build number on shift+double click
Ltitle.addEventListener('dblclick', (e) => {
this.emit('showBuild');
this.emit('showbuild');
return false;
}, false);

View file

@ -241,4 +241,9 @@ export default class AnchoredOSKView extends OSKView {
Ls.borderTop='1px solid gray';
}
}
public present() {
super.present();
this.legacyEvents.callEvent('show', {});
}
}

View file

@ -45,25 +45,23 @@ export default class FloatingOSKView extends OSKView {
super(config);
this.typedActivationModel.on('triggerChange', () => this.setDisplayPositioning());
this.typedActivationModel.on('triggerchange', () => this.setDisplayPositioning());
document.body.appendChild(this._Box);
// Add header element to OSK only for desktop browsers
this.titleBar = new TitleBar(this.titleDragHandler);
this.titleBar.on('help', () => {
this.emit('showHelp');
this.legacyEvents.callEvent('helpclick', {});
});
this.titleBar.on('config', () => {
this.emit('showConfig');
this.legacyEvents.callEvent('configclick', {});
});
this.titleBar.on('close', () => this.startHide(true));
this.titleBar.on('unpin', () => this.restorePosition(true));
this.resizeBar = new ResizeBar(this.resizeDragHandler);
this.resizeBar.on('showBuild', () => this.emit('showBuild'));
this.resizeBar.on('showbuild', () => this.emit('showbuild'));
this.headerView = this.titleBar;
@ -74,12 +72,10 @@ export default class FloatingOSKView extends OSKView {
if(titleBar && titleBar instanceof TitleBar) {
switch(eventName) {
case 'configclick':
case 'showConfig':
titleBar.configEnabled = this.listenerCount('showConfig') + this.legacyEvents.listenerCount('configclick') > 0;
titleBar.configEnabled = this.legacyEvents.listenerCount('configclick') > 0;
break;
case 'showHelp':
case 'helpclick':
titleBar.helpEnabled = this.listenerCount('showHelp') + this.legacyEvents.listenerCount('helpclick') > 0;
titleBar.helpEnabled = this.legacyEvents.listenerCount('helpclick') > 0;
break;
default:
return;
@ -90,8 +86,8 @@ export default class FloatingOSKView extends OSKView {
const listenerSpyNew = new EmitterListenerSpy(this);
const listenerSpyOld = new EmitterListenerSpy(this.legacyEvents);
for(let listenerSpy of [listenerSpyNew, listenerSpyOld]) {
listenerSpy.on('listenerAdded', onListenedEvent);
listenerSpy.on('listenerRemoved', onListenedEvent);
listenerSpy.on('listeneradded', onListenedEvent);
listenerSpy.on('listenerremoved', onListenedEvent);
}
this.loadPersistedLayout();
@ -153,7 +149,7 @@ export default class FloatingOSKView extends OSKView {
let isVisible = this._Visible;
let dragPromise = new ManagedPromise<void>();
this.emit('dragMove', dragPromise.corePromise);
this.emit('dragmove', dragPromise.corePromise);
this.loadPersistedLayout();
this.userPositioned=false;
@ -652,7 +648,7 @@ export default class FloatingOSKView extends OSKView {
}
this.dragPromise = new ManagedPromise<void>();
_this.emit('dragMove', this.dragPromise.corePromise);
_this.emit('dragmove', this.dragPromise.corePromise);
}
onDragMove(cumulativeX: number, cumulativeY: number) {
@ -715,7 +711,7 @@ export default class FloatingOSKView extends OSKView {
}
this.dragPromise = new ManagedPromise<void>();
_this.emit('resizeMove', this.dragPromise.corePromise);
_this.emit('resizemove', this.dragPromise.corePromise);
}
onDragMove(cumulativeX: number, cumulativeY: number) {

View file

@ -116,6 +116,12 @@ export default class InlinedOSKView extends OSKView {
return; // I3363 (Build 301)
}
public present() {
super.present();
this.legacyEvents.callEvent('show', {});
}
protected setDisplayPositioning() {
// no-op; an inlined OSK cannot control its own positioning.
}

View file

@ -21,7 +21,7 @@ import {
type SystemStoreMutationHandler
} from '@keymanapp/keyboard-processor';
import { createUnselectableElement, getAbsoluteX, getAbsoluteY, StylesheetManager } from 'keyman/engine/dom-utils';
import { LegacyEventEmitter } from 'keyman/engine/events';
import { EventListener, EventNames, LegacyEventEmitter } from 'keyman/engine/events';
import Configuration from '../config/viewConfiguration.js';
import Activator, { StaticActivator } from './activator.js';
@ -49,7 +49,9 @@ export interface LegacyOSKEventMap {
'helpclick'(obj: {});
'resizemove'(obj: {});
'show'(obj: {});
'hide'(obj: {});
'hide'(obj: {
HiddenByUser: boolean
});
}
/**
@ -64,41 +66,27 @@ export interface EventMap {
* Note: the following code block was originally used to integrate with the keyboard & input
* processors, but it requires entanglement with components external to this OSK module.
*/
'keyEvent': (event: KeyEvent) => void,
onshow(): void;
onhide(hiddenByUser: boolean): void;
'keyevent': (event: KeyEvent) => void,
/**
* Indicates that the globe key has either been pressed (`on` == `true`)
* or released (`on` == `false`).
*/
globeKey: (e: KeyElement, on: boolean) => void;
globekey: (e: KeyElement, on: boolean) => void;
/**
* A virtual keystroke corresponding to a "hide" command has been received.
*/
hideRequested: (key: KeyElement) => void;
/**
* This event is raised when the OSK's 'config' button is clicked.
* Adding a listener for the event will cause the 'config' button to be displayed for
* FloatingOSKView instances.
*/
showConfig: () => void;
/**
* This event is raised when the OSK's 'help' button is clicked.
* Adding a listener for the event will cause the 'help' button to be displayed for
* FloatingOSKView instances.
*/
showHelp: () => void;
hiderequested: (key: KeyElement) => void;
/**
* Signals the special command to display the engine's version + build number.
*/
showBuild: () => void;
showbuild: () => void;
// While the next two are near-duplicates of the legacy event `resizemove`, these
// have the advantage of providing a Promise for the end of the ongoing user
// interaction. We need that Promise for focus-management.
/**
* Signals that the OSK is being moved by the user via a drag operation.
@ -108,15 +96,14 @@ export interface EventMap {
* Note that position-restoration (unpinning the OSK) is treated as a drag-move
* event. It resolves near-instantly.
*/
dragMove: (promise: Promise<void>) => void;
dragmove: (promise: Promise<void>) => void;
/**
* Signals that the OSK is being resized via a drag operation (on a resize 'handle').
*
* The provided Promise will resolve once the resize operation is complete.
*/
resizeMove: (promise: Promise<void>) => void;
resizemove: (promise: Promise<void>) => void;
/**
* Signals that either the mouse or an active touchpoint is interacting with the OSK.
@ -125,7 +112,7 @@ export interface EventMap {
* Note that for touch events, more than one touchpoint may coexist, each with its own
* corresponding call of this event and corresponding `Promise`.
*/
pointerInteraction: (promise: Promise<void>) => void;
pointerinteraction: (promise: Promise<void>) => void;
}
export default abstract class OSKView extends EventEmitter<EventMap> implements MinimalCodesInterface {
@ -290,7 +277,7 @@ export default abstract class OSKView extends EventEmitter<EventMap> implements
}
this.mouseEnterPromise = new ManagedPromise<void>();
this.emit('pointerInteraction', this.mouseEnterPromise.corePromise);
this.emit('pointerinteraction', this.mouseEnterPromise.corePromise);
};
this._Box.onmouseleave = this._VKbdMouseLeave = (e) => {
@ -323,7 +310,7 @@ export default abstract class OSKView extends EventEmitter<EventMap> implements
this._boxBaseTouchStart = (e) => {
for(let i = 0; i < e.changedTouches.length; i++) {
let promise = this.touchEventPromiseManager.promiseForTouchpoint(e.changedTouches[i].identifier);
this.emit('pointerInteraction', promise.corePromise);
this.emit('pointerinteraction', promise.corePromise);
}
this.touchEventPromiseManager.maintainTouches(e.touches);
@ -814,11 +801,11 @@ export default abstract class OSKView extends EventEmitter<EventMap> implements
isEmbedded: this.config.isEmbedded
});
vkbd.on('keyEvent', (keyEvent) => this.emit('keyEvent', keyEvent));
vkbd.on('globeKey', (keyElement, on) => this.emit('globeKey', keyElement, on));
vkbd.on('hideRequested', (keyElement) => {
vkbd.on('keyevent', (keyEvent) => this.emit('keyevent', keyEvent));
vkbd.on('globekey', (keyElement, on) => this.emit('globekey', keyElement, on));
vkbd.on('hiderequested', (keyElement) => {
this.doHide(true);
this.emit('hideRequested', keyElement);
this.emit('hiderequested', keyElement);
});
// Set box class - OS and keyboard added for Build 360
@ -918,9 +905,8 @@ export default abstract class OSKView extends EventEmitter<EventMap> implements
this.setDisplayPositioning();
// Do this once all properties are set; that way, consumers can safely poll
// for position, size, etc.
this.emit('onshow');
// Each subclass is responsible for raising the 'show' event on its own, since
// certain ones supply extra information in their event param object.
}
/**
@ -1241,10 +1227,9 @@ export default abstract class OSKView extends EventEmitter<EventMap> implements
*
*/
doHide(hiddenByUser: boolean) {
this.emit('onhide', hiddenByUser);
const p={};
p['HiddenByUser']=hiddenByUser;
const p={
HiddenByUser: hiddenByUser
};
this.legacyEvents.callEvent('hide', p);
}
@ -1256,11 +1241,17 @@ export default abstract class OSKView extends EventEmitter<EventMap> implements
* @return {boolean}
* Description Wrapper function to add and identify OSK-specific event handlers
*/
addEventListener<T extends keyof LegacyOSKEventMap>(event: T, fn: (arg: {}) => any): void {
addEventListener<T extends keyof LegacyOSKEventMap>(
event: T,
fn: EventListener<LegacyOSKEventMap, T>
): void {
this.legacyEvents.addEventListener(event, fn);
}
removeEventListener<T extends keyof LegacyOSKEventMap>(event: T, fn: (arg: {}) => any): void {
removeEventListener<T extends keyof LegacyOSKEventMap>(
event: T,
fn: EventListener<LegacyOSKEventMap, T>
): void {
this.legacyEvents.removeEventListener(event, fn);
}
}

View file

@ -1,7 +1,7 @@
import Activator from './activator.js';
interface TriggerEventMap<Type> {
triggerChange: (trigger: Type) => void;
triggerchange: (trigger: Type) => void;
}
export default class TwoStateActivator<Type> extends Activator<TriggerEventMap<Type>> {
@ -40,7 +40,7 @@ export default class TwoStateActivator<Type> extends Activator<TriggerEventMap<T
this.checkState(oldState);
if(oldValue != value) {
this.emit('triggerChange', value);
this.emit('triggerchange', value);
}
}

View file

@ -88,11 +88,11 @@ interface EventMap {
* Note: the following code block was originally used to integrate with the keyboard & input
* processors, but it requires entanglement with components external to this OSK module.
*/
'keyEvent': (event: KeyEvent) => void,
'keyevent': (event: KeyEvent) => void,
'hideRequested': (keyElement: KeyElement) => void,
'hiderequested': (keyElement: KeyElement) => void,
'globeKey': (keyElement: KeyElement, on: boolean) => void
'globekey': (keyElement: KeyElement, on: boolean) => void
}
export default class VisualKeyboard extends EventEmitter<EventMap> implements KeyboardView {
@ -1861,10 +1861,10 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
optionKey(e: KeyElement, keyName: string, keyDown: boolean) {
if (keyName.indexOf('K_LOPT') >= 0) {
this.emit('globeKey', e, keyDown);
this.emit('globekey', e, keyDown);
} else if (keyName.indexOf('K_ROPT') >= 0) {
if (keyDown) {
this.emit('hideRequested', e);
this.emit('hiderequested', e);
}
}
};
@ -1949,6 +1949,6 @@ export default class VisualKeyboard extends EventEmitter<EventMap> implements Ke
return true;
}
this.emit('keyEvent', keyEvent);
this.emit('keyevent', keyEvent);
}
}

View file

@ -1,6 +1,5 @@
import { Keyboard, KeyboardLoaderBase as KeyboardLoader } from "@keymanapp/keyboard-processor";
import EventEmitter from "eventemitter3";
import { type PathConfiguration } from "keyman/engine/paths";
import KeyboardStub from "./keyboardStub.js";
@ -34,12 +33,12 @@ interface EventMap {
* to denote the first added stub to facilitate auto-activation of the first
* keyboard to be registered.
*/
stubAdded: (stub: KeyboardStub) => void;
stubadded: (stub: KeyboardStub) => void;
/**
* Indicates that the specified Keyboard has just been added to the cache.
*/
keyboardAdded: (keyboard: Keyboard) => void;
keyboardadded: (keyboard: Keyboard) => void;
}
export default class StubAndKeyboardCache extends EventEmitter<EventMap> {
@ -117,7 +116,7 @@ export default class StubAndKeyboardCache extends EventEmitter<EventMap> {
const keyboardID = prefixed(keyboard.id);
this.keyboardTable[keyboardID] = keyboard;
this.emit('keyboardAdded', keyboard);
this.emit('keyboardadded', keyboard);
}
fetchKeyboardForStub(stub: KeyboardStub) : Promise<Keyboard> {
@ -182,7 +181,7 @@ export default class StubAndKeyboardCache extends EventEmitter<EventMap> {
const stubTable = this.stubSetTable[keyboardID] = this.stubSetTable[keyboardID] ?? {};
stubTable[stub.KLC] = stub;
this.emit('stubAdded', stub);
this.emit('stubadded', stub);
}
findMatchingStub(stub: KeyboardStub) {

View file

@ -59,25 +59,21 @@ describe('OSK events', function () {
// Setup complete.
// Hide the OSK + check for related event
let hideStub = sinon.fake();
let legacyHideStub = sinon.fake();
osk.once('onhide', hideStub);
osk.legacyEvents.addEventListener('hide', legacyHideStub);
osk.activationModel.enabled = false;
assert.isTrue(hideStub.calledOnce);
assert.isTrue(legacyHideStub.calledOnce);
// The hide actually occurs on a Promise's completion.
// Adding the 'await' makes this clearer during test maintenance.
await Promise.resolve();
// Show the OSK + check for related event
let showStub = sinon.fake();
// let legacyShowStub = sinon.fake();
osk.once('onshow', showStub);
// osk.once('legacyevent', legacyShowStub);
let legacyShowStub = sinon.fake();
osk.legacyEvents.addEventListener('show', legacyShowStub);
osk.activationModel.enabled = true;
assert.isTrue(showStub.calledOnce);
// // Only called (at present) for "Floating" OSK views, given the event's parameterization.
// assert.isTrue(legacyShowStub.calledOnce);
// assert.equal(legacyShowStub.firstCall.args[0], 'osk.show');
assert.isTrue(legacyShowStub.calledOnce);
});
});

View file

@ -15,8 +15,8 @@ describe("EmitterListenerSpy", () => {
const fakeAddListener = sinon.fake();
const fakeRemoveListener = sinon.fake();
emitterSpy.on('listenerAdded', fakeAddListener);
emitterSpy.on('listenerRemoved', fakeRemoveListener);
emitterSpy.on('listeneradded', fakeAddListener);
emitterSpy.on('listenerremoved', fakeRemoveListener);
emitter.on('event', fakeEventHandler);
assert.isTrue(fakeAddListener.calledOnce);
@ -35,8 +35,8 @@ describe("EmitterListenerSpy", () => {
const fakeAddListener = sinon.fake();
const fakeRemoveListener = sinon.fake();
emitterSpy.on('listenerAdded', fakeAddListener);
emitterSpy.on('listenerRemoved', fakeRemoveListener);
emitterSpy.on('listeneradded', fakeAddListener);
emitterSpy.on('listenerremoved', fakeRemoveListener);
emitter.addEventListener('event', fakeEventHandler);
assert.isTrue(fakeAddListener.calledOnce);

View file

@ -95,7 +95,7 @@ describe("Activators", () => {
const activateStub = sinon.fake();
const triggerStub = sinon.fake();
activator.on('activate', activateStub);
activator.on('triggerChange', triggerStub);
activator.on('triggerchange', triggerStub);
activator.activationTrigger = "foo"; // using a string, just 'cause.
@ -154,7 +154,7 @@ describe("Activators", () => {
it("'triggerchange' event generation", () => {
const activator = new TwoStateActivator();
const stub = sinon.fake();
activator.on('triggerChange', stub);
activator.on('triggerchange', stub);
activator.enabled = false;
@ -219,7 +219,7 @@ describe("Activators", () => {
it("'activationTrigger' as object", () => {
const activator = new TwoStateActivator();
const stub = sinon.fake();
activator.on('triggerChange', stub);
activator.on('triggerchange', stub);
const object = {a: {b: {c: "def"}, g: "hi"}, jk: ["l"]};

View file

@ -97,7 +97,7 @@ function setOSK(mode) {
keyboard = setKeyboard('us');
}
currentOSK.activeKeyboard = keyboard;
currentOSK.on('keyEvent', (event) => {
currentOSK.on('keyevent', (event) => {
let eventText = JSON.stringify(event, (key, value) => {
switch(key) {
case 'srcKeyboard':

View file

@ -182,7 +182,7 @@ export class Scribe extends EventEmitter {
initHooks(recordingElement: HTMLElement) {
let recorderScribe = this;
keyman.hardKeyboard.on('keyEvent', (e) => {
keyman.hardKeyboard.on('keyevent', (e) => {
let in_output = outputTargetForElement(recordingElement);
if(!in_output || keyman.contextManager.activeTarget != in_output) {
return;
@ -198,7 +198,7 @@ export class Scribe extends EventEmitter {
}, 1);
});
keyman.osk.on('keyEvent', (e) => {
keyman.osk.on('keyevent', (e) => {
let in_output = outputTargetForElement(recordingElement);
if(!in_output || keyman.contextManager.activeTarget != in_output) {
return;