diff --git a/common/web/keyboard-processor/src/keyboards/activeLayout.ts b/common/web/keyboard-processor/src/keyboards/activeLayout.ts index 96ec667f764..eaecc50e2eb 100644 --- a/common/web/keyboard-processor/src/keyboards/activeLayout.ts +++ b/common/web/keyboard-processor/src/keyboards/activeLayout.ts @@ -108,7 +108,7 @@ export class ActiveKeyBase { proportionalWidth: number; // While they're only valid on ActiveKey, spec'ing them here makes references more concise within the OSK. - sk?: ActiveKey[]; + sk?: ActiveSubKey[]; multitap?: ActiveSubKey[]; flick?: TouchLayout.TouchLayoutFlick; @@ -214,6 +214,24 @@ export class ActiveKeyBase { return new KeyEvent(val); } + constructor(); + constructor(spec: LayoutKey | LayoutSubKey, layout: ActiveLayout, displayLayer: string); + constructor(spec?: LayoutKey | LayoutSubKey, layout?: ActiveLayout, displayLayer?: string) { + // First things first: this class's fields are designed to match that of the spec. + Object.assign(this, spec); + + if(!this.text && typeof this.id == 'string') { + this.text = ActiveKey.unicodeIDToText(this.id); + } + + this.displayLayer = displayLayer; + this.layer = this.layer || displayLayer; + + // Compute the key's base KeyEvent properties for use in future event generation + // It's actually somewhat expensive to do this at the start, so we do a lazy-init. + this._baseKeyEvent = () => this.constructBaseKeyEvent(layout, displayLayer); + } + /** * Converts key IDs of the U_* form to their corresponding UTF-16 text. * If an ID not matching the pattern is received, returns null. @@ -318,66 +336,102 @@ export class ActiveKeyBase { rawKey.text ||= ActiveKey.DEFAULT_KEY.text; } - static polyfill(key: LayoutKey, keyboard: Keyboard, layout: ActiveLayout, displayLayer: string, analysisFlagObj?: AnalysisMetadata) { - analysisFlagObj ||= { - hasFlicks: false, - hasLongpresses: false, - hasMultitaps: false - } + @Enumerable + private constructBaseKeyEvent(layout: ActiveLayout, displayLayer: string) { + // Get key name and keyboard shift state (needed only for default layouts and physical keyboard handling) + // Note - virtual keys should be treated case-insensitive, so we force uppercasing here. + let layer = this.layer || displayLayer || ''; + let keyName= this.id ? this.id.toUpperCase() : null; - // Add class functions to the existing layout object, allowing it to act as an ActiveLayout. - let dummy = new ActiveKeyBase(); - let proto = Object.getPrototypeOf(dummy); - - for(let prop in dummy) { - if(!key.hasOwnProperty(prop)) { - let descriptor = Object.getOwnPropertyDescriptor(proto, prop); - if(descriptor) { - // It's a computed property! Copy the descriptor onto the key's object. - Object.defineProperty(key, prop, descriptor); - } else { - key[prop] = dummy[prop]; + // Start: mirrors _GetKeyEventProperties + + // First check the virtual key, and process shift, control, alt or function keys + let props: KeyEventSpec = { + // Override key shift state if specified for key in layout (corrected for popup keys KMEW-93) + Lmodifiers: Codes.getModifierState(layer), + Lstates: Codes.getStateFromLayer(layer), + Lcode: keyName ? Codes.keyCodes[keyName] : 0, + LisVirtualKey: true, + vkCode: 0, + kName: keyName, + kLayer: layer, + kbdLayer: displayLayer, + kNextLayer: this.nextlayer, + device: null, + isSynthetic: true + }; + + let Lkc: KeyEvent = new KeyEvent(props); + + if(layout.keyboard) { + let keyboard = layout.keyboard; + + // Include *limited* support for mnemonic keyboards (Sept 2012) + // If a touch layout has been defined for a mnemonic keyout, do not perform mnemonic mapping for rules on touch devices. + if(keyboard.isMnemonic && !(layout.isDefault && layout.formFactor != 'desktop')) { + if(Lkc.Lcode != Codes.keyCodes['K_SPACE']) { // exception required, March 2013 + // Jan 2019 - interesting that 'K_SPACE' also affects the caps-state check... + Lkc.vkCode = Lkc.Lcode; + this.isMnemonic = true; } + } else { + Lkc.vkCode=Lkc.Lcode; + } + + // Support version 1.0 KeymanWeb keyboards that do not define positional vs mnemonic + if(!keyboard.definesPositionalOrMnemonic) { + Lkc.Lcode = KeyMapping._USKeyCodeToCharCode(Lkc); + Lkc.LisVirtualKey=false; } } - if(!key.text && typeof key.id == 'string') { - key.text = ActiveKey.unicodeIDToText(key.id); + return Lkc; + } +} + + +export class ActiveKey extends ActiveKeyBase implements LayoutKey { + public getSubkey(coreID: string): ActiveSubKey { + if(this.sk) { + for(let key of this.sk) { + if(key.coreID == coreID) { + return key; + } + } } + return null; + } + + constructor(); + constructor(spec: LayoutKey, layout: ActiveLayout, displayLayer: string); + constructor(spec?: LayoutKey, layout?: ActiveLayout, displayLayer?: string) { + super(spec, layout, displayLayer); + // Ensure subkeys are also properly extended. - if(key.sk) { - analysisFlagObj.hasLongpresses = true; - for(let subkey of key.sk) { - ActiveSubKey.polyfill(subkey, keyboard, layout, displayLayer, analysisFlagObj); + const sk = this.sk; + if(sk) { + for(let i=0; i < sk.length; i++) { + sk[i] = new ActiveSubKey(sk[i], layout, displayLayer); } } // Also multitap keys. - if(key.multitap) { - analysisFlagObj.hasMultitaps = true; - for(let mtKey of key.multitap) { - ActiveSubKey.polyfill(mtKey, keyboard, layout, displayLayer, analysisFlagObj); + const multitap = this.multitap; + if(multitap) { + for(let i=0; i < multitap.length; i++) { + multitap[i] = new ActiveSubKey(multitap[i], layout, displayLayer); } } - - if(key.flick) { - analysisFlagObj.hasFlicks = true; - for(let flickKey in key.flick) { - ActiveSubKey.polyfill(key.flick[flickKey as keyof TouchLayoutFlick], keyboard, layout, displayLayer, analysisFlagObj); + const flick = this.flick; + if(flick) { + for(let flickKey in flick) { + flick[flickKey] = new ActiveSubKey(flick[flickKey], layout, displayLayer); } } - let aKey = key as ActiveKey; - aKey.displayLayer = displayLayer; - aKey.layer = aKey.layer || displayLayer; - - ActiveKeyBase.determineHint(aKey, layout.defaultHint); - - // Compute the key's base KeyEvent properties for use in future event generation - // It's actually somewhat expensive to do this at the start, so we do a lazy-init. - aKey._baseKeyEvent = () => aKey.constructBaseKeyEvent(layout, displayLayer); + ActiveKey.determineHint(this, layout.defaultHint); } private static determineHint(spec: ActiveKey, defaultHint: TouchLayout.TouchLayoutDefaultHint): void { @@ -433,73 +487,6 @@ export class ActiveKeyBase { return; } } - - @Enumerable - private constructBaseKeyEvent(layout: ActiveLayout, displayLayer: string) { - // Get key name and keyboard shift state (needed only for default layouts and physical keyboard handling) - // Note - virtual keys should be treated case-insensitive, so we force uppercasing here. - let layer = this.layer || displayLayer || ''; - let keyName= this.id ? this.id.toUpperCase() : null; - - // Start: mirrors _GetKeyEventProperties - - // First check the virtual key, and process shift, control, alt or function keys - let props: KeyEventSpec = { - // Override key shift state if specified for key in layout (corrected for popup keys KMEW-93) - Lmodifiers: Codes.getModifierState(layer), - Lstates: Codes.getStateFromLayer(layer), - Lcode: keyName ? Codes.keyCodes[keyName] : 0, - LisVirtualKey: true, - vkCode: 0, - kName: keyName, - kLayer: layer, - kbdLayer: displayLayer, - kNextLayer: this.nextlayer, - device: null, - isSynthetic: true - }; - - let Lkc: KeyEvent = new KeyEvent(props); - - if(layout.keyboard) { - let keyboard = layout.keyboard; - - // Include *limited* support for mnemonic keyboards (Sept 2012) - // If a touch layout has been defined for a mnemonic keyout, do not perform mnemonic mapping for rules on touch devices. - if(keyboard.isMnemonic && !(layout.isDefault && layout.formFactor != 'desktop')) { - if(Lkc.Lcode != Codes.keyCodes['K_SPACE']) { // exception required, March 2013 - // Jan 2019 - interesting that 'K_SPACE' also affects the caps-state check... - Lkc.vkCode = Lkc.Lcode; - this.isMnemonic = true; - } - } else { - Lkc.vkCode=Lkc.Lcode; - } - - // Support version 1.0 KeymanWeb keyboards that do not define positional vs mnemonic - if(!keyboard.definesPositionalOrMnemonic) { - Lkc.Lcode = KeyMapping._USKeyCodeToCharCode(Lkc); - Lkc.LisVirtualKey=false; - } - } - - return Lkc; - } -} - - -export class ActiveKey extends ActiveKeyBase implements LayoutKey { - public getSubkey(coreID: string): ActiveKey { - if(this.sk) { - for(let key of this.sk) { - if(key.coreID == coreID) { - return key; - } - } - } - - return null; - } } @@ -574,7 +561,18 @@ export class ActiveRow implements LayoutRow { break; } - ActiveKey.polyfill(key, keyboard, layout, displayLayer, analysisFlagObj); + const processedKey = new ActiveKey(key, layout, displayLayer); + keys[j] = processedKey; + + if(processedKey.sk) { + analysisFlagObj.hasLongpresses = true; + } + if(processedKey.multitap) { + analysisFlagObj.hasMultitaps = true; + } + if(processedKey.flick) { + analysisFlagObj.hasFlicks = true; + } } /* The calculations here are effectively 'virtualized'. When used with the OSK, the VisualKeyboard @@ -889,8 +887,8 @@ export class ActiveLayout implements LayoutFormFactor{ defaultShift.multitap = [{...Layouts.dfltShiftToCaps}, {...Layouts.dfltShiftToDefault}] as ActiveSubKey[]; shiftShift.multitap = [{...Layouts.dfltShiftToCaps}, {...Layouts.dfltShiftToShift}] as ActiveSubKey[]; - defaultShift.multitap.forEach((sk) => ActiveSubKey.polyfill(sk, keyboard, aLayout, 'default')); - shiftShift .multitap.forEach((sk) => ActiveSubKey.polyfill(sk, keyboard, aLayout, 'shift')); + defaultShift.multitap.forEach((sk, index) => defaultShift[index] = new ActiveSubKey(sk, aLayout, 'default')); + shiftShift .multitap.forEach((sk, index) => shiftShift[index] = new ActiveSubKey(sk, aLayout, 'shift')); } // else no default shift -> caps multitaps. } diff --git a/web/src/engine/osk/src/input/gestures/browser/oskSubKey.ts b/web/src/engine/osk/src/input/gestures/browser/oskSubKey.ts index bf7d5eb5f4f..142fb106efd 100644 --- a/web/src/engine/osk/src/input/gestures/browser/oskSubKey.ts +++ b/web/src/engine/osk/src/input/gestures/browser/oskSubKey.ts @@ -24,7 +24,6 @@ export default class OSKSubKey extends OSKKey { let spec = this.spec; let kDiv=document.createElement('div'); - let tKey = osk.getDefaultKeyObject(); let ks=kDiv.style; kDiv.className='kmw-key-square-ex'; diff --git a/web/src/engine/osk/src/keyboard-layout/oskLayerGroup.ts b/web/src/engine/osk/src/keyboard-layout/oskLayerGroup.ts index 6d7065e6b79..751ba73a94c 100644 --- a/web/src/engine/osk/src/keyboard-layout/oskLayerGroup.ts +++ b/web/src/engine/osk/src/keyboard-layout/oskLayerGroup.ts @@ -54,10 +54,6 @@ export default class OSKLayerGroup { layers=layout['layer']; - // Set key default attributes (must use exportable names!) - var tKey=vkbd.getDefaultKeyObject(); - tKey['fontsize']=ls.fontSize; - for(n=0; n implements Ke } } } - - /** - * Returns the default properties for a key object, used to construct - * both a base keyboard key and popup keys - * - * @return {Object} An object that contains default key properties - */ - getDefaultKeyObject(): ActiveKey { - const baseKeyObject: LayoutKey = {...ActiveKey.DEFAULT_KEY}; - ActiveKey.polyfill(baseKeyObject, this.layoutKeyboard, this.kbdLayout, this.layerId); - return baseKeyObject as ActiveKey; - }; //#endregion //#region OSK touch handlers