feat(web): touch-layout layer spec processing now deferred until needed

This commit is contained in:
Joshua A. Horton 2024-04-19 10:43:02 +07:00
parent fdd4c2a6a7
commit bb0420109f
4 changed files with 44 additions and 26 deletions

View file

@ -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<layers.length; n++) {
let layer=layers[n];
let rows=layer['row'];
let rows=layer.row;
let i: number;
for(i=rows.length-1; i>=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<layers.length; n++) {
ActiveLayer.polyfill(layers[n], aLayout);
layerMap[layers[n].id] = layers[n] as ActiveLayer;
}
// After all layers are preprocessed...
aLayout.layerMap = layerMap;
// The default-layer shift key & shift-layer shift key on mobile platforms should have a
// default multitap re: a 'caps' layer under select conditions.
//
// Note: whether or not any other keys have multitaps doesn't matter here. Just THESE.
if(formFactor != 'desktop' && !!layout.layer.find((entry) => 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;
}

View file

@ -33,7 +33,7 @@ export interface LayoutLayer extends LayoutLayerBase {
aligned?: boolean
};
export interface LayoutFormFactor extends Omit<LayoutFormFactorSpec, 'layer'> {
export interface LayoutFormFactor extends LayoutFormFactorSpec {
};
export type LayoutSpec = {

View file

@ -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;
}

View file

@ -1294,6 +1294,12 @@ export default class VisualKeyboard extends EventEmitter<EventMap> 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;