mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 00:15:32 +00:00
Merge branch 'epic/web-core' into test/common/baseline
This commit is contained in:
commit
5666b4296e
26 changed files with 120 additions and 89 deletions
|
|
@ -247,7 +247,7 @@ function deregisterModel(modelID) {
|
|||
}
|
||||
|
||||
function enableSuggestions(model, suggestionType) {
|
||||
// Set the options first so that KMW's ModelManager can properly handle model enablement states
|
||||
// Set the options first so that KMW's ModelCache can properly handle model enablement states
|
||||
// the moment we actually register the new model.
|
||||
// Use console_debug
|
||||
console_debug('enableSuggestions(model, maySuggest='+suggestionType+')');
|
||||
|
|
|
|||
|
|
@ -62,6 +62,15 @@ class TestNode {
|
|||
TestNode.OpenNodes.push(this.id);
|
||||
}
|
||||
|
||||
private escape(message: string): string {
|
||||
// TeamCity escaping rules for: ' | [ ] \n \r \uNNNN
|
||||
// See: https://www.jetbrains.com/help/teamcity/service-messages.html#Escaped+Values
|
||||
return message?.replace(/['|\[\]]/g, (matched) => `|${matched}`)
|
||||
.replace(/\n/g, '|n')
|
||||
.replace(/\r/g, '|r')
|
||||
.replace(/[\u0080-\uFFFF]/g, c => `|0x${c.charCodeAt(0).toString(16).padStart(4, '0')}`) ?? '';
|
||||
}
|
||||
|
||||
private getTestResult(result: TestResult): { msgTitle: string, details: string } {
|
||||
if (!result) {
|
||||
return null;
|
||||
|
|
@ -72,9 +81,9 @@ class TestNode {
|
|||
case 'failed':
|
||||
case 'interrupted':
|
||||
case 'timedOut':
|
||||
return { msgTitle: 'testFailed', details: `message='${result.error?.message}' details='${result.error?.value ?? result.error?.cause}'` };
|
||||
return { msgTitle: 'testFailed', details: `message='${this.escape(result.error?.message)}' details='${this.escape(result.error?.value ?? result.error?.cause)}'` };
|
||||
case 'skipped':
|
||||
return { msgTitle: 'testIgnored', details: `message='${result.annotations?.toString() ?? ''}'` };
|
||||
return { msgTitle: 'testIgnored', details: `message='${this.escape(result.annotations?.toString()) ?? ''}'` };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -327,7 +327,7 @@ function toHex(theString) {
|
|||
}
|
||||
|
||||
function enableSuggestions(model, mayPredict, mayCorrect) {
|
||||
// Set the options first so that KMW's ModelManager can properly handle model enablement states
|
||||
// Set the options first so that KMW's ModelCache can properly handle model enablement states
|
||||
// the moment we actually register the new model.
|
||||
keyman.core.languageProcessor.mayPredict = mayPredict;
|
||||
keyman.core.languageProcessor.mayCorrect = mayCorrect;
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ function test-headless() {
|
|||
TEST_FOLDER=$1
|
||||
TEST_BASE="${KEYMAN_ROOT}/web/src/test/auto/headless/"
|
||||
TEST_EXTENSIONS=${2:-}
|
||||
if [ ! -z "${2:-}" ]; then
|
||||
if [[ ! -z "${2:-}" ]]; then
|
||||
TEST_BASE="${KEYMAN_ROOT}/web/build/test/headless/"
|
||||
|
||||
# Ensure the compiled tests are available.
|
||||
|
|
@ -104,6 +104,7 @@ function test-headless() {
|
|||
echo "##teamcity[flowStarted flowId='unit_tests']"
|
||||
fi
|
||||
if [[ -n "${TEST_EXTENSIONS}" ]]; then
|
||||
# file extension of test files
|
||||
TEST_OPTS+=(--extension "${TEST_EXTENSIONS}")
|
||||
fi
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,12 @@ type KeyboardState = {
|
|||
baseLayout: string
|
||||
}
|
||||
|
||||
const DOM_KEY_LOCATION = {
|
||||
STANDARD: 0,
|
||||
LEFT: 1,
|
||||
RIGHT: 2,
|
||||
};
|
||||
|
||||
// Important: the following two lines should not cause a compile error if left uncommented.
|
||||
// let dummy1: KeyboardProcessor;
|
||||
// let dummy2: KeyboardState = dummy1;
|
||||
|
|
@ -51,9 +57,9 @@ export function _GetEventKeyCode(e: KeyboardEvent) {
|
|||
* @param {KeyboardEvent} e Event object
|
||||
* @param {KeyboardState} keyboardState Keyboard state object
|
||||
* @param {DeviceSpec} device Device object
|
||||
* @return {KeyEvent} KeymanWeb KeyEvent object, or null
|
||||
* for duplicate/spurious events or if
|
||||
* there is no key code.
|
||||
*
|
||||
* @return {KeyEvent} KeymanWeb KeyEvent object, or null for duplicate/spurious
|
||||
* events or if there is no key code.
|
||||
*/
|
||||
export function preprocessKeyboardEvent(e: KeyboardEvent, keyboardState: KeyboardState, device: DeviceSpec): KeyEvent {
|
||||
if(e.cancelBubble === true) {
|
||||
|
|
@ -104,17 +110,21 @@ export function preprocessKeyboardEvent(e: KeyboardEvent, keyboardState: Keyboar
|
|||
*/
|
||||
|
||||
let curModState = 0x0000;
|
||||
curModState |= (e.getModifierState("Shift") ? 0x10 : 0);
|
||||
curModState |= (e.getModifierState("Shift") ? ModifierKeyConstants.K_SHIFTFLAG : 0);
|
||||
|
||||
if(e.getModifierState("Control")) {
|
||||
curModState |= ((e.location != 0 && ctrlEvent) ?
|
||||
(e.location == 1 ? ModifierKeyConstants.LCTRLFLAG : ModifierKeyConstants.RCTRLFLAG) : // Condition 1
|
||||
prevModState & 0x0003 /* LCTRLFLAG | RCTRLFLAG */); // Condition 2
|
||||
curModState |= ((e.location != DOM_KEY_LOCATION.STANDARD && ctrlEvent)
|
||||
? (e.location == DOM_KEY_LOCATION.LEFT
|
||||
? ModifierKeyConstants.LCTRLFLAG
|
||||
: ModifierKeyConstants.RCTRLFLAG) // Condition 1
|
||||
: prevModState & (ModifierKeyConstants.LCTRLFLAG | ModifierKeyConstants.RCTRLFLAG)); // Condition 2
|
||||
}
|
||||
if(e.getModifierState("Alt")) {
|
||||
curModState |= ((e.location != 0 && altEvent) ?
|
||||
(e.location == 1 ? ModifierKeyConstants.LALTFLAG : ModifierKeyConstants.RALTFLAG) : // Condition 1
|
||||
prevModState & 0x000C /* LALTFLAG | RALTFLAG */); // Condition 2
|
||||
curModState |= ((e.location != DOM_KEY_LOCATION.STANDARD && altEvent)
|
||||
? (e.location == DOM_KEY_LOCATION.LEFT
|
||||
? ModifierKeyConstants.LALTFLAG
|
||||
: ModifierKeyConstants.RALTFLAG) // Condition 1
|
||||
: prevModState & (ModifierKeyConstants.LALTFLAG | ModifierKeyConstants.RALTFLAG)); // Condition 2
|
||||
}
|
||||
|
||||
// Stage 2 - detect state key information. It can be looked up per keypress with no issue.
|
||||
|
|
@ -143,7 +153,7 @@ export function preprocessKeyboardEvent(e: KeyboardEvent, keyboardState: Keyboar
|
|||
curModState &= ~ altGrMask;
|
||||
}
|
||||
// Perform basic filtering for Windows-based ALT_GR emulation on European keyboards.
|
||||
if(curModState & ModifierKeyConstants.RALTFLAG) {
|
||||
if((curModState & ModifierKeyConstants.RALTFLAG) != 0) {
|
||||
curModState &= ~ModifierKeyConstants.LCTRLFLAG;
|
||||
}
|
||||
|
||||
|
|
@ -151,7 +161,7 @@ export function preprocessKeyboardEvent(e: KeyboardEvent, keyboardState: Keyboar
|
|||
const modifierBitmasks = Codes.modifierBitmasks;
|
||||
const activeKeyboard = keyboardState.activeKeyboard;
|
||||
let Lmodifiers: number;
|
||||
if(activeKeyboard && activeKeyboard.isChiral) {
|
||||
if(activeKeyboard?.isChiral) {
|
||||
Lmodifiers = curModState & modifierBitmasks.CHIRAL;
|
||||
|
||||
// Note for future - embedding a kill switch here would facilitate disabling AltGr / Right-alt simulation.
|
||||
|
|
@ -162,9 +172,9 @@ export function preprocessKeyboardEvent(e: KeyboardEvent, keyboardState: Keyboar
|
|||
} else {
|
||||
// No need to sim AltGr here; we don't need chiral ALTs.
|
||||
Lmodifiers =
|
||||
(curModState & 0x10) | // SHIFT
|
||||
((curModState & (ModifierKeyConstants.LCTRLFLAG | ModifierKeyConstants.RCTRLFLAG)) ? 0x20 : 0) |
|
||||
((curModState & (ModifierKeyConstants.LALTFLAG | ModifierKeyConstants.RALTFLAG)) ? 0x40 : 0);
|
||||
(curModState & ModifierKeyConstants.K_SHIFTFLAG) |
|
||||
((curModState & (ModifierKeyConstants.LCTRLFLAG | ModifierKeyConstants.RCTRLFLAG)) != 0 ? ModifierKeyConstants.K_CTRLFLAG : 0) |
|
||||
((curModState & (ModifierKeyConstants.LALTFLAG | ModifierKeyConstants.RALTFLAG)) != 0 ? ModifierKeyConstants.K_ALTFLAG : 0);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -210,7 +220,7 @@ export function preprocessKeyboardEvent(e: KeyboardEvent, keyboardState: Keyboar
|
|||
return processedEvent;
|
||||
}
|
||||
|
||||
export default class HardwareEventKeyboard extends HardKeyboardBase {
|
||||
export class HardwareEventKeyboard extends HardKeyboardBase {
|
||||
private readonly hardDevice: DeviceSpec;
|
||||
|
||||
// Needed properties & methods:
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import * as views from './viewsAnchorpoint.js';
|
|||
import { BrowserConfiguration, BrowserInitOptionDefaults, BrowserInitOptionSpec } from './configuration.js';
|
||||
import { ContextManager } from './contextManager.js';
|
||||
import DefaultBrowserRules from './defaultBrowserRules.js';
|
||||
import HardwareEventKeyboard from './hardwareEventKeyboard.js';
|
||||
import { HardwareEventKeyboard } from './hardwareEventKeyboard.js';
|
||||
import { FocusStateAPIObject } from './context/focusAssistant.js';
|
||||
import { PageIntegrationHandlers } from './context/pageIntegrationHandlers.js';
|
||||
import { LanguageMenu } from './languageMenu.js';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
export { BrowserConfiguration, BrowserInitOptionSpec } from './configuration.js';
|
||||
export { ContextManager, KeyboardCookie } from "./contextManager.js";
|
||||
export { preprocessKeyboardEvent, default as HardwareEventKeyboard } from './hardwareEventKeyboard.js';
|
||||
export { preprocessKeyboardEvent, HardwareEventKeyboard } from './hardwareEventKeyboard.js';
|
||||
|
||||
export { KeymanEngine } from './keymanEngine.js';
|
||||
export { KeyboardInterface } from './keyboardInterface.js';
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
export { CloudQueryResult, default as QueryEngine } from './queryEngine.js';
|
||||
export { default as RequesterInterface } from './requesterInterface.js';
|
||||
export { CloudQueryResult, CloudQueryEngine as QueryEngine } from './queryEngine.js';
|
||||
export { CloudRequesterInterface as RequesterInterface } from './requesterInterface.js';
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ import { EventEmitter } from 'eventemitter3';
|
|||
|
||||
import { PathConfiguration } from 'keyman/engine/interfaces';
|
||||
|
||||
import { default as KeyboardStub, ErrorStub, KeyboardAPISpec } from '../keyboardStub.js';
|
||||
import { KeyboardStub, ErrorStub, KeyboardAPISpec } from '../keyboardStub.js';
|
||||
import { LanguageAPIPropertySpec, ManagedPromise, Version } from 'keyman/engine/keyboard';
|
||||
import CloudRequesterInterface from './requesterInterface.js';
|
||||
import { CloudRequesterInterface } from './requesterInterface.js';
|
||||
|
||||
// For when the API call straight-up times out.
|
||||
export const CLOUD_TIMEOUT_ERR = "The Cloud API request timed out.";
|
||||
|
|
@ -56,7 +56,7 @@ interface EventMap {
|
|||
'unboundregister': (registration: ReturnType<CloudQueryEngine['_registerCore']>) => void
|
||||
}
|
||||
|
||||
export default class CloudQueryEngine extends EventEmitter<EventMap> {
|
||||
export class CloudQueryEngine extends EventEmitter<EventMap> {
|
||||
private cloudResolutionPromises: Map<number, ManagedPromise<KeyboardStub[] | LanguageAPIPropertySpec[]>> = new Map();
|
||||
|
||||
private _languageListPromise: ManagedPromise<LanguageAPIPropertySpec[]>;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { ManagedPromise } from 'keyman/engine/keyboard';
|
||||
|
||||
export default interface CloudRequesterInterface {
|
||||
export interface CloudRequesterInterface {
|
||||
request<T>(query: string): {
|
||||
promise: ManagedPromise<T>,
|
||||
queryId: number
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { ManagedPromise } from 'keyman/engine/keyboard';
|
||||
import CloudRequesterInterface from './cloud/requesterInterface.js';
|
||||
import { CloudRequesterInterface } from './cloud/requesterInterface.js';
|
||||
import { CLOUD_MALFORMED_OBJECT_ERR, CLOUD_TIMEOUT_ERR, CLOUD_STUB_REGISTRATION_ERR } from './cloud/queryEngine.js';
|
||||
|
||||
export default class DOMCloudRequester implements CloudRequesterInterface {
|
||||
export class DOMCloudRequester implements CloudRequesterInterface {
|
||||
private readonly fileLocal: boolean;
|
||||
|
||||
constructor(fileLocal: boolean = false) {
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@
|
|||
export {
|
||||
ErrorStub,
|
||||
type KeyboardAPISpec,
|
||||
default as KeyboardStub,
|
||||
KeyboardStub,
|
||||
mergeAndResolveStubPromises,
|
||||
RawKeyboardStub,
|
||||
REGIONS,
|
||||
REGION_CODES
|
||||
} from './keyboardStub.js';
|
||||
export { default as StubAndKeyboardCache, toPrefixedKeyboardId, toUnprefixedKeyboardId } from './stubAndKeyboardCache.js';
|
||||
export { CloudQueryResult, default as CloudQueryEngine } from './cloud/queryEngine.js';
|
||||
export { default as CloudRequesterInterface } from './cloud/requesterInterface.js';
|
||||
export { default as KeyboardRequisitioner } from './keyboardRequisitioner.js';
|
||||
export { default as ModelCache } from './modelCache.js';
|
||||
export { default as DOMCloudRequester } from './domCloudRequester.js';
|
||||
export { StubAndKeyboardCache, toPrefixedKeyboardId, toUnprefixedKeyboardId } from './stubAndKeyboardCache.js';
|
||||
export { CloudQueryResult, CloudQueryEngine } from './cloud/queryEngine.js';
|
||||
export { CloudRequesterInterface } from './cloud/requesterInterface.js';
|
||||
export { KeyboardRequisitioner } from './keyboardRequisitioner.js';
|
||||
export { ModelCache } from './modelCache.js';
|
||||
export { DOMCloudRequester } from './domCloudRequester.js';
|
||||
|
|
@ -17,7 +17,7 @@ import {
|
|||
mergeAndResolveStubPromises,
|
||||
toUnprefixedKeyboardId as unprefixed
|
||||
} from "./index.js";
|
||||
import { default as CloudRequesterInterface } from "./cloud/requesterInterface.js";
|
||||
import { CloudRequesterInterface } from "./cloud/requesterInterface.js";
|
||||
import { rejectErrorStubs } from "./keyboardStub.js";
|
||||
|
||||
class CloudRequestEntry {
|
||||
|
|
@ -89,7 +89,7 @@ function isUniqueRequest(cache: StubAndKeyboardCache, cloudList: {id: string, la
|
|||
};
|
||||
|
||||
// TODO: Move to the keyboard-cache child project - we can test it headlessly there!
|
||||
export default class KeyboardRequisitioner {
|
||||
export class KeyboardRequisitioner {
|
||||
readonly cache: StubAndKeyboardCache;
|
||||
readonly cloudQueryEngine: CloudQueryEngine;
|
||||
readonly pathConfig: PathConfiguration;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ function configureFilePathing(path: string, configurationBasePath: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export default class KeyboardStub extends KeyboardProperties {
|
||||
export class KeyboardStub extends KeyboardProperties {
|
||||
KR: string;
|
||||
KRC: string;
|
||||
KF: string;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { ModelSpec } from 'keyman/engine/interfaces';
|
||||
|
||||
export default class ModelManager {
|
||||
export class ModelCache {
|
||||
// Tracks registered models by ID.
|
||||
private registeredModels: {[id: string]: ModelSpec} = {};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { type Keyboard, JSKeyboard, KeyboardLoaderBase as KeyboardLoader, KMXKeyboard } from "keyman/engine/keyboard";
|
||||
import { EventEmitter } from "eventemitter3";
|
||||
|
||||
import KeyboardStub from "./keyboardStub.js";
|
||||
import { KeyboardStub } from "./keyboardStub.js";
|
||||
|
||||
const KEYBOARD_PREFIX = "Keyboard_";
|
||||
|
||||
function prefixed(text: string) {
|
||||
export function toPrefixedKeyboardId(text: string) {
|
||||
if(!text.startsWith(KEYBOARD_PREFIX)) {
|
||||
return KEYBOARD_PREFIX + text;
|
||||
} else {
|
||||
|
|
@ -13,9 +13,7 @@ function prefixed(text: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export {prefixed as toPrefixedKeyboardId};
|
||||
|
||||
function withoutPrefix(text: string) {
|
||||
export function toUnprefixedKeyboardId(text: string) {
|
||||
if(text.startsWith(KEYBOARD_PREFIX)) {
|
||||
return text.substring(KEYBOARD_PREFIX.length);
|
||||
} else {
|
||||
|
|
@ -23,8 +21,6 @@ function withoutPrefix(text: string) {
|
|||
}
|
||||
}
|
||||
|
||||
export {withoutPrefix as toUnprefixedKeyboardId};
|
||||
|
||||
interface EventMap {
|
||||
/**
|
||||
* Indicates that the specified stub has just been registered within the cache.
|
||||
|
|
@ -41,7 +37,7 @@ interface EventMap {
|
|||
keyboardadded: (keyboard: Keyboard) => void;
|
||||
}
|
||||
|
||||
export default class StubAndKeyboardCache extends EventEmitter<EventMap> {
|
||||
export class StubAndKeyboardCache extends EventEmitter<EventMap> {
|
||||
private stubSetTable: Record<string, Record<string, KeyboardStub>> = {};
|
||||
private keyboardTable: Record<string, Keyboard | Promise<Keyboard>> = {};
|
||||
|
||||
|
|
@ -70,7 +66,7 @@ export default class StubAndKeyboardCache extends EventEmitter<EventMap> {
|
|||
if(!keyboardID) {
|
||||
return null;
|
||||
}
|
||||
const entry = this.keyboardTable[prefixed(keyboardID)];
|
||||
const entry = this.keyboardTable[toPrefixedKeyboardId(keyboardID)];
|
||||
|
||||
// Unit testing may 'trip up' in the DOM, as bundled versions of a class from one bundled
|
||||
// module will fail against an `instanceof` expecting the version bundled in a second.
|
||||
|
|
@ -123,7 +119,7 @@ export default class StubAndKeyboardCache extends EventEmitter<EventMap> {
|
|||
}
|
||||
|
||||
addKeyboard(keyboard: Keyboard) {
|
||||
const keyboardID = prefixed(keyboard.id);
|
||||
const keyboardID = toPrefixedKeyboardId(keyboard.id);
|
||||
this.keyboardTable[keyboardID] = keyboard;
|
||||
|
||||
this.emit('keyboardadded', keyboard);
|
||||
|
|
@ -138,7 +134,7 @@ export default class StubAndKeyboardCache extends EventEmitter<EventMap> {
|
|||
throw new Error("Keyboard ID must be specified");
|
||||
}
|
||||
|
||||
keyboardID = prefixed(keyboardID);
|
||||
keyboardID = toPrefixedKeyboardId(keyboardID);
|
||||
|
||||
const cachedEntry = this.keyboardTable[keyboardID];
|
||||
return cachedEntry instanceof Promise;
|
||||
|
|
@ -153,7 +149,7 @@ export default class StubAndKeyboardCache extends EventEmitter<EventMap> {
|
|||
throw new Error("Cannot load keyboards; this cache was configured without a loader");
|
||||
}
|
||||
|
||||
keyboardID = prefixed(keyboardID);
|
||||
keyboardID = toPrefixedKeyboardId(keyboardID);
|
||||
|
||||
const cachedEntry = this.keyboardTable[keyboardID];
|
||||
if(cachedEntry instanceof JSKeyboard) {
|
||||
|
|
@ -164,11 +160,11 @@ export default class StubAndKeyboardCache extends EventEmitter<EventMap> {
|
|||
|
||||
const stub = this.getStub(keyboardID, null);
|
||||
if(!stub) {
|
||||
throw new Error(`No stub for ${withoutPrefix(keyboardID)} has been registered`);
|
||||
throw new Error(`No stub for ${toUnprefixedKeyboardId(keyboardID)} has been registered`);
|
||||
}
|
||||
|
||||
if(!stub.filename) {
|
||||
throw new Error(`The registered stub for ${withoutPrefix(keyboardID)} lacks a path to the main keyboard file`);
|
||||
throw new Error(`The registered stub for ${toUnprefixedKeyboardId(keyboardID)} lacks a path to the main keyboard file`);
|
||||
}
|
||||
|
||||
const promise = this.keyboardLoader.loadKeyboardFromStub(stub);
|
||||
|
|
@ -189,7 +185,7 @@ export default class StubAndKeyboardCache extends EventEmitter<EventMap> {
|
|||
}
|
||||
|
||||
addStub(stub: KeyboardStub) {
|
||||
const keyboardID = prefixed(stub.KI);
|
||||
const keyboardID = toPrefixedKeyboardId(stub.KI);
|
||||
const stubTable = this.stubSetTable[keyboardID] = this.stubSetTable[keyboardID] ?? {};
|
||||
stubTable[stub.KLC] = stub;
|
||||
|
||||
|
|
@ -213,7 +209,7 @@ export default class StubAndKeyboardCache extends EventEmitter<EventMap> {
|
|||
}
|
||||
|
||||
if(keyboardID) {
|
||||
keyboardID = prefixed(keyboardID);
|
||||
keyboardID = toPrefixedKeyboardId(keyboardID);
|
||||
}
|
||||
|
||||
const stubTable = this.stubSetTable[keyboardID] ?? {};
|
||||
|
|
@ -238,7 +234,7 @@ export default class StubAndKeyboardCache extends EventEmitter<EventMap> {
|
|||
* If `false`, only forgets the metadata (stubs).
|
||||
*/
|
||||
forgetKeyboard(keyboard: string | JSKeyboard, purge: boolean = false) {
|
||||
const id: string = (keyboard instanceof JSKeyboard) ? keyboard.id : prefixed(keyboard);
|
||||
const id: string = (keyboard instanceof JSKeyboard) ? keyboard.id : toPrefixedKeyboardId(keyboard);
|
||||
|
||||
if(this.stubSetTable[id]) {
|
||||
delete this.stubSetTable[id];
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ export { JSKeyboard, LayoutState } from "./keyboards/jsKeyboard.js";
|
|||
export { KeyboardMinimalInterface } from './keyboards/keyboardMinimalInterface.js';
|
||||
export { KMXKeyboard } from './keyboards/kmxKeyboard.js';
|
||||
export { KeyboardHarness, KeyboardKeymanGlobal, MinimalCodesInterface, MinimalKeymanGlobal } from "./keyboards/keyboardHarness.js";
|
||||
export { Keyboard, KeyboardLoaderBase } from "./keyboards/keyboardLoaderBase.js";
|
||||
export { NotifyEventCode, Keyboard, KeyboardLoaderBase } from "./keyboards/keyboardLoaderBase.js";
|
||||
export { KeyboardLoadErrorBuilder, KeyboardMissingError, KeyboardScriptError, KeyboardDownloadError, InvalidKeyboardError } from './keyboards/keyboardLoadError.js'
|
||||
export { BeepHandler, EventMap, KeyboardProcessor } from "./keyboards/keyboardProcessor.js";
|
||||
export {
|
||||
|
|
|
|||
|
|
@ -14,6 +14,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';
|
||||
|
||||
/**
|
||||
* Stores preprocessed properties of a keyboard for quick retrieval later.
|
||||
|
|
@ -356,11 +357,12 @@ export class JSKeyboard {
|
|||
/**
|
||||
* Notifies keyboard of keystroke or other event
|
||||
*
|
||||
* @param {number} command event code (16,17,18) or 0
|
||||
* @param {TextStore} textStore textStore
|
||||
* @param {number} data 1 or 0
|
||||
* @param {NotifyEventCode} command event code (16,17,18) or 0
|
||||
* @param {TextStore} textStore textStore
|
||||
* @param {number} data 1 for KeyDown or FocusReceived,
|
||||
* 0 for KeyUp or FocusLost
|
||||
*/
|
||||
public notify(command: number, textStore: TextStore, data: number): void { // I2187
|
||||
public notify(command: NotifyEventCode, textStore: TextStore, data: number): void { // I2187
|
||||
// Good example use case - the Japanese CJK-picker keyboard
|
||||
if(typeof(this.scriptObject['KNS']) == 'function') {
|
||||
this.scriptObject['KNS'](command, textStore, data);
|
||||
|
|
|
|||
|
|
@ -4,6 +4,14 @@ 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';
|
||||
|
||||
export enum NotifyEventCode {
|
||||
FocusEvent = 0,
|
||||
ShiftKey = Codes.keyCodes.K_SHIFT,
|
||||
ControlKey = Codes.keyCodes.K_CONTROL,
|
||||
AltKey = Codes.keyCodes.K_ALT,
|
||||
};
|
||||
|
||||
export type KeyboardStub = KeyboardProperties & { filename: string };
|
||||
export type Keyboard = JSKeyboard | KMXKeyboard;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { ActiveKey, ActiveSubKey } from './activeLayout.js';
|
|||
import { StateKeyMap } from './stateKeyMap.js';
|
||||
import { KeyEvent } from '../keyEvent.js';
|
||||
import { TextStore } from '../textStore.js';
|
||||
import { NotifyEventCode } from './keyboardLoaderBase.js';
|
||||
|
||||
/**
|
||||
* Acts as a wrapper class for KMX(+) Keyman keyboards
|
||||
|
|
@ -99,12 +100,15 @@ export class KMXKeyboard {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {number} eventCode event code (16,17,18) or 0 // TODO-web-core: document meaning of these! (#15290)
|
||||
* @param {TextStore} textStore textStore
|
||||
* @param {number} data 1 or 0
|
||||
* Notifies keyboard of keystroke or other event
|
||||
*
|
||||
* @param {NotifyEventCode} eventCode key code (16-18: Shift, Control or Alt),
|
||||
* or 0 for focus
|
||||
* @param {TextStore} textStore textStore
|
||||
* @param {number} data 1 for KeyDown or FocusReceived,
|
||||
* 0 for KeyUp or FocusLost
|
||||
*/
|
||||
public notify(eventCode: 16|17|18|0, textStore: TextStore, data: number) { // I2187
|
||||
public notify(eventCode: NotifyEventCode, textStore: TextStore, data: number): void { // I2187
|
||||
// TODO-web-core: do we need to support this? (#15290)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -168,26 +168,29 @@ export class InputProcessor {
|
|||
*/
|
||||
private _processKeyEvent(keyEvent: KeyEvent, textStore: TextStore): ProcessorAction {
|
||||
const formFactor = keyEvent.device.formFactor;
|
||||
const fromOSK = keyEvent.isSynthetic;
|
||||
|
||||
// The default OSK layout for desktop devices does not include nextlayer info, relying on modifier detection here.
|
||||
// The default OSK layout for desktop devices does not include nextlayer info, relying on
|
||||
// modifier detection here.
|
||||
// It's the OSK equivalent to doModifierPress on 'desktop' form factors.
|
||||
if((formFactor == DeviceSpec.FormFactor.Desktop || !this.activeKeyboard || (this.activeKeyboard instanceof JSKeyboard && this.activeKeyboard.usesDesktopLayoutOnDevice(keyEvent.device))) && fromOSK) {
|
||||
if ((formFactor == DeviceSpec.FormFactor.Desktop || !this.activeKeyboard ||
|
||||
(this.activeKeyboard instanceof JSKeyboard &&
|
||||
this.activeKeyboard.usesDesktopLayoutOnDevice(keyEvent.device))) &&
|
||||
keyEvent.isSynthetic
|
||||
) {
|
||||
// If it's a desktop OSK style and this triggers a layer change,
|
||||
// a modifier key was clicked. No output expected, so it's safe to instantly exit.
|
||||
if(this.keyboardProcessor.selectLayer(keyEvent)) {
|
||||
if (this.keyboardProcessor.selectLayer(keyEvent)) {
|
||||
return new ProcessorAction();
|
||||
}
|
||||
}
|
||||
|
||||
// Will handle keystroke-based non-layer change modifier & state keys, mapping them through the physical keyboard's version
|
||||
// of state management. `doModifierPress` must always run.
|
||||
if (this.keyboardProcessor.doModifierPress(keyEvent, textStore, !fromOSK)) {
|
||||
// Will handle keystroke-based non-layer change modifier & state keys, mapping them through
|
||||
// the physical keyboard's version of state management. `doModifierPress` must always run.
|
||||
const wasModifierPress = this.keyboardProcessor.doModifierPress(keyEvent, textStore, !keyEvent.isSynthetic);
|
||||
if (wasModifierPress && !keyEvent.isSynthetic) {
|
||||
// If run on a desktop platform, we know that modifier & state key presses may not
|
||||
// produce output, so we may make an immediate return safely.
|
||||
if(!fromOSK) {
|
||||
return new ProcessorAction();
|
||||
}
|
||||
return new ProcessorAction();
|
||||
}
|
||||
|
||||
// If suggestions exist AND space is pressed, accept the suggestion and do not process the keystroke.
|
||||
|
|
|
|||
|
|
@ -360,7 +360,7 @@ export class LanguageProcessor extends EventEmitter<LanguageProcessorEventMap> {
|
|||
/**
|
||||
* Retrieves the context and output state of KMW immediately before the prediction with
|
||||
* token `id` was generated. Must correspond to a 'recent' one, as only so many are stored
|
||||
* in `ModelManager`'s history buffer.
|
||||
* in `ModelCache`'s history buffer.
|
||||
* @param id A unique identifier corresponding to a recent `Transcription`.
|
||||
* @returns The matching `Transcription`, or `null` none is found.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ const testsToFix = {
|
|||
'k_0700___caps_lock.kmn',
|
||||
'k_0701___caps_control.kmn',
|
||||
'k_0807___enter_invalidates_context.kmn',
|
||||
'k_0813___deadkey_cancelled_by_arrow.kmn',
|
||||
'k_0813___deadkey_cancelled_by_arrow.kmn', // Keyman Engine for Web does not interpret arrow keys - #15397
|
||||
],
|
||||
// TODO: fix these tests (#15342)
|
||||
'.js': [
|
||||
|
|
@ -81,7 +81,7 @@ const testsToFix = {
|
|||
'k_0807___enter_invalidates_context.kmn',
|
||||
'k_0808___nul_and_context.kmn', // js only
|
||||
'k_0810___nul_and_index.kmn', // js only
|
||||
'k_0813___deadkey_cancelled_by_arrow.kmn',
|
||||
'k_0813___deadkey_cancelled_by_arrow.kmn', // Keyman Engine for Web does not interpret arrow keys - #15397
|
||||
]
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ under `src`.
|
|||
|
||||
For example, `src/test/auto/headless/engine/js-processor` are the tests
|
||||
for `src/engine/js-processor` and will be run from
|
||||
`src/engine/js-processor/build.sh`.
|
||||
`src/engine/build.sh`.
|
||||
|
|
|
|||
|
|
@ -30,12 +30,10 @@ describe('CoreKeyboardProcessor', function () {
|
|||
const item = new KM_Core.instance.km_core_context_item();
|
||||
if (isMarker) {
|
||||
item.marker = c as number;
|
||||
} else if (typeof c == 'number') {
|
||||
item.character = c;
|
||||
} else {
|
||||
if (typeof (c) == 'number') {
|
||||
item.character = c;
|
||||
} else {
|
||||
item.character = c.codePointAt(0);
|
||||
}
|
||||
item.character = c.codePointAt(0);
|
||||
}
|
||||
contextItems.push_back(item);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { ManagedPromise } from 'keyman/engine/keyboard';
|
||||
import CloudRequesterInterface from '../../../../engine/src/keyboard-storage/cloud/requesterInterface.js';
|
||||
import { CloudRequesterInterface } from '../../../../engine/src/keyboard-storage/cloud/requesterInterface.js';
|
||||
import {
|
||||
CLOUD_TIMEOUT_ERR,
|
||||
CLOUD_STUB_REGISTRATION_ERR,
|
||||
CloudQueryResult,
|
||||
default as CloudQueryEngine
|
||||
CloudQueryEngine
|
||||
} from '../../../../engine/src/keyboard-storage/cloud/queryEngine.js';
|
||||
|
||||
import fs from 'node:fs';
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue