diff --git a/common/web/keyboard-processor/src/keyboards/activeLayout.ts b/common/web/keyboard-processor/src/keyboards/activeLayout.ts index 35a472b056..6089ce3b0a 100644 --- a/common/web/keyboard-processor/src/keyboards/activeLayout.ts +++ b/common/web/keyboard-processor/src/keyboards/activeLayout.ts @@ -745,7 +745,11 @@ export class ActiveLayer implements LayoutLayer { } export class ActiveLayout implements LayoutFormFactor{ - layer: ActiveLayer[]; + /** + * Holds all layer specifications for the layout. There is no guarantee that they + * have been fully preprocessed. + */ + layer: TouchLayerSpec[]; font: string; keyLabels: boolean; isDefault?: boolean; @@ -766,8 +770,25 @@ export class ActiveLayout implements LayoutFormFactor{ } + /** + * Returns a fully preprocessed version of the specified layer spec. + * @param layerId + * @returns + */ @Enumerable getLayer(layerId: string): ActiveLayer { + if(!this.layerMap[layerId]) { + const spec = this.layer.find((layerSpec) => layerSpec.id == layerId); + if(!spec) { + return null; + } + + // Prepare the layer-spec for actual use. + ActiveLayer.sanitize(spec); + ActiveLayer.polyfill(spec, this); + this.layerMap[layerId] = spec as ActiveLayer; + } + return this.layerMap[layerId]; } @@ -786,10 +807,10 @@ export class ActiveLayout implements LayoutFormFactor{ static correctLayerEmptyRowBug(layers: LayoutLayer[]) { for(let n=0; n=0; i--) { - if(!Array.isArray(rows[i]['key']) || rows[i]['key'].length == 0) { + if(!Array.isArray(rows[i].key) || rows[i].key.length == 0) { rows.splice(i, 1) } } @@ -798,10 +819,6 @@ export class ActiveLayout implements LayoutFormFactor{ static sanitize(rawLayout: TouchLayoutSpec) { ActiveLayout.correctLayerEmptyRowBug(rawLayout.layer); - - for(const layer of rawLayout.layer) { - ActiveLayer.sanitize(layer); - } } /** @@ -843,11 +860,8 @@ export class ActiveLayout implements LayoutFormFactor{ } // Create a separate OSK div for each OSK layer, only one of which will ever be visible - var n: number; let layerMap: {[layerId: string]: ActiveLayer} = {}; - let layers=layout.layer; - // Add class functions to the existing layout object, allowing it to act as an ActiveLayout. let dummy = new ActiveLayout(); for(let key in dummy) { @@ -856,24 +870,20 @@ export class ActiveLayout implements LayoutFormFactor{ } } - let aLayout = layout as ActiveLayout; + let aLayout = layout as unknown as ActiveLayout; aLayout.keyboard = keyboard; aLayout.formFactor = formFactor; - - for(n=0; n entry.id == 'caps')) { - const defaultLayer = layout.layer.find((entry) => entry.id == 'default') as ActiveLayer; - const shiftLayer = layout.layer.find((entry) => entry.id == 'shift') as ActiveLayer; + // Triggers preprocessing for both default and shift layers. They're the + // most-frequently referenced, at least. + const defaultLayer = aLayout.getLayer('default') as ActiveLayer; + const shiftLayer = aLayout.getLayer('shift') as ActiveLayer; const defaultShift = defaultLayer.getKey('K_SHIFT'); const shiftShift = shiftLayer ?.getKey('K_SHIFT'); @@ -898,7 +908,7 @@ export class ActiveLayout implements LayoutFormFactor{ aLayout.hasLongpresses = analysisMetadata.hasLongpresses; aLayout.hasMultitaps = analysisMetadata.hasMultitaps; - aLayout.layerMap = layerMap; + // All layers are lazy-processed, with the usual processing applied when first referenced. return aLayout; } diff --git a/common/web/keyboard-processor/src/keyboards/defaultLayouts.ts b/common/web/keyboard-processor/src/keyboards/defaultLayouts.ts index e1efa9847c..172b63d070 100644 --- a/common/web/keyboard-processor/src/keyboards/defaultLayouts.ts +++ b/common/web/keyboard-processor/src/keyboards/defaultLayouts.ts @@ -33,7 +33,7 @@ export interface LayoutLayer extends LayoutLayerBase { aligned?: boolean }; -export interface LayoutFormFactor extends Omit { +export interface LayoutFormFactor extends LayoutFormFactorSpec { }; export type LayoutSpec = { diff --git a/common/web/keyboard-processor/src/keyboards/keyboard.ts b/common/web/keyboard-processor/src/keyboards/keyboard.ts index b91c9f03a8..9a6c39d533 100644 --- a/common/web/keyboard-processor/src/keyboards/keyboard.ts +++ b/common/web/keyboard-processor/src/keyboards/keyboard.ts @@ -4,7 +4,7 @@ import { ActiveKey, ActiveLayout, ActiveSubKey } from "./activeLayout.js"; import KeyEvent from "../text/keyEvent.js"; import type OutputTarget from "../text/outputTarget.js"; import { TouchLayout } from "@keymanapp/common-types"; -import TouchLayoutSpec = TouchLayout.TouchLayoutPlatform; +type TouchLayoutSpec = TouchLayout.TouchLayoutPlatform & { isDefault?: boolean}; import type { ComplexKeyboardStore } from "../text/kbdInterface.js"; @@ -403,7 +403,7 @@ export default class Keyboard { // Final check - do we construct a layout, or is this a case where helpText / insertHelpHTML should take over? if(rawSpecifications) { // Now to generate a layout from our raw specifications. - let layout = this._layouts[formFactor] = Layouts.buildDefaultLayout(rawSpecifications, this, formFactor) as ActiveLayout; + let layout: TouchLayoutSpec = this._layouts[formFactor] = Layouts.buildDefaultLayout(rawSpecifications, this, formFactor); layout.isDefault = true; return layout; } else { @@ -425,11 +425,13 @@ export default class Keyboard { if(rawLayout) { // Prevents accidentally reprocessing layouts; it's a simple enough check. if(this.layoutStates[formFactor] == LayoutState.NOT_LOADED) { - rawLayout = ActiveLayout.polyfill(rawLayout, this, formFactor); + const layout = ActiveLayout.polyfill(rawLayout, this, formFactor); this.layoutStates[formFactor] = LayoutState.POLYFILLED; + return layout; + } else { + return rawLayout as unknown as ActiveLayout; } - return rawLayout as ActiveLayout; } else { return null; } diff --git a/web/src/engine/osk/src/visualKeyboard.ts b/web/src/engine/osk/src/visualKeyboard.ts index 09a9b092d3..6b1cd50075 100644 --- a/web/src/engine/osk/src/visualKeyboard.ts +++ b/web/src/engine/osk/src/visualKeyboard.ts @@ -1294,6 +1294,12 @@ export default class VisualKeyboard extends EventEmitter implements Ke return allottedHeight; } + /* + Note: these may not be fully preprocessed yet! + + However, any "empty row bug" preprocessing has been applied, and that's + what we care about here. + */ const layers = this.layerGroup.spec.layer; let oskHeight = 0;