mirror of
https://github.com/keymanapp/keyman.git
synced 2026-09-24 08:37:42 +00:00
parent
5cf1a1fad3
commit
d7ce2191bf
10 changed files with 95 additions and 49 deletions
|
|
@ -13,8 +13,7 @@ import { BUILDER_SECTION } from "./builder-section.js";
|
|||
* List of layers, the <layers> element
|
||||
*/
|
||||
interface BUILDER_LAYR_LIST {
|
||||
flags: number;
|
||||
hardware: number; // str - hardware name, see #7986
|
||||
hardware: number; // hardware indicator
|
||||
layer: number; // index of first layer in the list, in the
|
||||
count: number; // number of layer entries in the list
|
||||
minDeviceWidth: number; // width in millimeters
|
||||
|
|
@ -83,8 +82,7 @@ export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l
|
|||
|
||||
layr.lists = kmxplus.layr.lists.map((list) => {
|
||||
const blist: BUILDER_LAYR_LIST = {
|
||||
flags: list.flags,
|
||||
hardware: build_strs_index(sect_strs, list.hardware),
|
||||
hardware: list.hardware,
|
||||
layer: null, // to be set below
|
||||
_layers: list.layers,
|
||||
count: list.layers.length,
|
||||
|
|
@ -94,11 +92,9 @@ export function build_layr(kmxplus: KMXPlusData, sect_strs: BUILDER_STRS, sect_l
|
|||
});
|
||||
// now sort the lists
|
||||
layr.lists.sort((a, b) => {
|
||||
const aform = a.flags & constants.layr_list_flags_mask_form;
|
||||
const bform = b.flags & constants.layr_list_flags_mask_form;
|
||||
if (aform < bform) {
|
||||
if (a.hardware < b.hardware) {
|
||||
return -1;
|
||||
} else if (aform > bform) {
|
||||
} else if (a.hardware > b.hardware) {
|
||||
return 1;
|
||||
}
|
||||
if (a.minDeviceWidth < b.minDeviceWidth) {
|
||||
|
|
|
|||
|
|
@ -225,8 +225,7 @@ export class Disp extends Section {
|
|||
* In-memory `<layers>`
|
||||
*/
|
||||
export class LayrList {
|
||||
flags: number;
|
||||
hardware: StrsItem;
|
||||
hardware: number;
|
||||
layers: LayrEntry[] = [];
|
||||
minDeviceWidth: number; // millimeters
|
||||
};
|
||||
|
|
@ -507,8 +506,7 @@ export class KMXPlusFile extends KMXFile {
|
|||
});
|
||||
|
||||
this.COMP_PLUS_LAYR_LIST = new r.Struct({
|
||||
flags: r.uint32le,
|
||||
hardware: r.uint32le, //str
|
||||
hardware: r.uint32le, //enum
|
||||
layer: r.uint32le, // index into layers
|
||||
count: r.uint32le,
|
||||
minDeviceWidth: r.uint32le, // integer: millimeters
|
||||
|
|
|
|||
|
|
@ -29,9 +29,11 @@
|
|||
#define LDML_KEY2_KEY_FLAGS_GAP 0x2
|
||||
#define LDML_KEY2_KEY_FLAGS_NOTRANSFORM 0x4
|
||||
#define LDML_KEYS_FLAGS_EXTEND 0x1
|
||||
#define LDML_LAYR_LIST_FLAGS_HARDWARE 0x0
|
||||
#define LDML_LAYR_LIST_FLAGS_MASK_FORM 0x1
|
||||
#define LDML_LAYR_LIST_FLAGS_TOUCH 0x1
|
||||
#define LDML_LAYR_LIST_HARDWARE_ABNT2 0x1
|
||||
#define LDML_LAYR_LIST_HARDWARE_ISO 0x2
|
||||
#define LDML_LAYR_LIST_HARDWARE_JIS 0x3
|
||||
#define LDML_LAYR_LIST_HARDWARE_TOUCH 0x0
|
||||
#define LDML_LAYR_LIST_HARDWARE_US 0x4
|
||||
#define LDML_LENGTH_BKSP 0xC
|
||||
#define LDML_LENGTH_BKSP_ITEM 0x10
|
||||
#define LDML_LENGTH_DISP 0x10
|
||||
|
|
@ -51,7 +53,7 @@
|
|||
#define LDML_LENGTH_LAYR 0x18
|
||||
#define LDML_LENGTH_LAYR_ENTRY 0x10
|
||||
#define LDML_LENGTH_LAYR_KEY 0x4
|
||||
#define LDML_LENGTH_LAYR_LIST 0x14
|
||||
#define LDML_LENGTH_LAYR_LIST 0x10
|
||||
#define LDML_LENGTH_LAYR_ROW 0x8
|
||||
#define LDML_LENGTH_LIST 0x10
|
||||
#define LDML_LENGTH_LIST_INDEX 0x4
|
||||
|
|
|
|||
|
|
@ -281,19 +281,39 @@ class Constants {
|
|||
/**
|
||||
* Length of each layer list in the 'layr' section variable part
|
||||
*/
|
||||
readonly length_layr_list = 20;
|
||||
readonly length_layr_list = 16;
|
||||
/**
|
||||
* bitmask for the 'form' field of the layr.list[].flags bitfield
|
||||
* for the 'hardware' field indicating a touch keyboard, non-hardware
|
||||
*/
|
||||
readonly layr_list_flags_mask_form = 1;
|
||||
readonly layr_list_hardware_touch = 0;
|
||||
/**
|
||||
* hardware layout: value for the 'form' field of the layr.list[].flags
|
||||
* for the 'hardware' field indicating an abnt2 layout
|
||||
*/
|
||||
readonly layr_list_flags_hardware = 0;
|
||||
readonly layr_list_hardware_abnt2 = 1;
|
||||
/**
|
||||
* touch layout: value for the 'form' field of the layr.list[].flags
|
||||
* for the 'hardware' field indicating an iso layout
|
||||
*/
|
||||
readonly layr_list_flags_touch = 1;
|
||||
readonly layr_list_hardware_iso = 2;
|
||||
/**
|
||||
* for the 'hardware' field indicating a jis layout
|
||||
*/
|
||||
readonly layr_list_hardware_jis = 3;
|
||||
/**
|
||||
* for the 'hardware' field indicating a us layout
|
||||
*/
|
||||
readonly layr_list_hardware_us = 4;
|
||||
/**
|
||||
* Convenience map of layr_list_hardware field values
|
||||
*/
|
||||
readonly layr_list_hardware_map: Map<String, number> = new Map(
|
||||
[
|
||||
["touch", this.layr_list_hardware_touch],
|
||||
["abnt2", this.layr_list_hardware_abnt2],
|
||||
["iso", this.layr_list_hardware_iso],
|
||||
["jis", this.layr_list_hardware_jis],
|
||||
["us", this.layr_list_hardware_us],
|
||||
]
|
||||
);
|
||||
/**
|
||||
* Length of each layer entry in the 'layr' section variable part
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -369,8 +369,7 @@ static_assert(sizeof(struct COMP_KMXPLUS_DISP) == LDML_LENGTH_DISP, "mismatched
|
|||
------------------------------------------------------------------ */
|
||||
|
||||
struct COMP_KMXPLUS_LAYR_LIST {
|
||||
KMX_DWORD flags;
|
||||
KMXPLUS_STR hardware;
|
||||
KMX_DWORD hardware;
|
||||
KMX_DWORD layer;
|
||||
KMX_DWORD count;
|
||||
KMX_DWORD minDeviceWidth;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import Layr = KMXPlus.Layr;
|
|||
import LayrEntry = KMXPlus.LayrEntry;
|
||||
import LayrList = KMXPlus.LayrList;
|
||||
import LayrRow = KMXPlus.LayrRow;
|
||||
// import USVirtualKeyMap = Constants.USVirtualKeyMap;
|
||||
|
||||
export class LayrCompiler extends SectionCompiler {
|
||||
|
||||
|
|
@ -18,11 +17,48 @@ export class LayrCompiler extends SectionCompiler {
|
|||
|
||||
public validate() {
|
||||
let valid = true;
|
||||
if(!this.keyboard.layers?.[0]?.layer?.length) {
|
||||
if (!this.keyboard.layers?.[0]?.layer?.length) {
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_MustBeAtLeastOneLayerElement());
|
||||
}
|
||||
// TODO-LDML
|
||||
let hardwareLayers = 0;
|
||||
let touchLayers = 0;
|
||||
this.keyboard.layers.forEach(({ hardware, form }) => {
|
||||
// TODO-LDML: in the future >1 hardware layer may be allowed, check for duplicates
|
||||
if (form === 'touch') {
|
||||
touchLayers++;
|
||||
if (hardware) {
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({
|
||||
errorText: `Not allowed: hardware="${hardware}" with layers form="touch"`
|
||||
}));
|
||||
} else if (touchLayers > 1) { // TODO-LDML: revisit if spec changes
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_MustHaveAtMostOneLayersElementPerForm({ form }));
|
||||
}
|
||||
} else if (form === 'hardware') {
|
||||
hardwareLayers++;
|
||||
if (!hardware) {
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({
|
||||
errorText: `on layers form="hardware", missing required hardware= attribute.`
|
||||
}));
|
||||
} else if (!constants.layr_list_hardware_map.get(hardware)) {
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({
|
||||
errorText: `Unknown hardware layout id: hardware="${hardware}"`
|
||||
}));
|
||||
} else if (hardwareLayers > 1) { // TODO-LDML: revisit if spec changes
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_MustHaveAtMostOneLayersElementPerForm({ form }));
|
||||
}
|
||||
} else {
|
||||
valid = false;
|
||||
this.callbacks.reportMessage(CompilerMessages.Error_InvalidFile({
|
||||
errorText: `Invalid form="${form}" on layers element`
|
||||
}));
|
||||
}
|
||||
});
|
||||
return valid;
|
||||
}
|
||||
|
||||
|
|
@ -30,16 +66,17 @@ export class LayrCompiler extends SectionCompiler {
|
|||
const sect = new Layr();
|
||||
|
||||
sect.lists = this.keyboard.layers.map((layers) => {
|
||||
const list : LayrList = {
|
||||
flags: 0,
|
||||
hardware: sections.strs.allocString(layers.hardware),
|
||||
const hardware = constants.layr_list_hardware_map.get(layers.hardware || 'touch');
|
||||
// Don't need to check 'form' because it is checked in validate
|
||||
const list: LayrList = {
|
||||
hardware,
|
||||
minDeviceWidth: layers.minDeviceWidth || 0,
|
||||
layers: layers.layer.map((layer) => {
|
||||
const entry : LayrEntry = {
|
||||
const entry: LayrEntry = {
|
||||
id: sections.strs.allocString(layer.id),
|
||||
modifier: sections.strs.allocString(layer.modifier),
|
||||
rows: layer.row.map((row) => {
|
||||
const erow : LayrRow = {
|
||||
const erow: LayrRow = {
|
||||
keys: row.keys.split(' ').map((id) => sections.strs.allocString(id)),
|
||||
};
|
||||
return erow;
|
||||
|
|
@ -49,9 +86,6 @@ export class LayrCompiler extends SectionCompiler {
|
|||
return entry;
|
||||
}),
|
||||
};
|
||||
if (layers.form === 'touch') {
|
||||
list.flags |= constants.layr_list_flags_touch;
|
||||
}
|
||||
return list;
|
||||
});
|
||||
return sect;
|
||||
|
|
|
|||
|
|
@ -79,7 +79,11 @@ export class CompilerMessages {
|
|||
|
||||
static Error_KeyMissingToGapOrSwitch = (o:{keyId: string}) =>
|
||||
m(this.ERROR_KeyMissingToGapOrSwitch, `key id='${o.keyId}' must have either to=, gap=, or switch=.`);
|
||||
static ERROR_KeyMissingToGapOrSwitch = SevError | 0x0011;
|
||||
static ERROR_KeyMissingToGapOrSwitch = SevError | 0x0011;
|
||||
|
||||
static Error_MustHaveAtMostOneLayersElementPerForm = (o:{form: string}) => m(this.ERROR_MustHaveAtMostOneLayersElementPerForm,
|
||||
`Must have at most one layers element with form=${o.form}`);
|
||||
static ERROR_MustHaveAtMostOneLayersElementPerForm = SevError | 0x0012;
|
||||
|
||||
static severityName(code: number): string {
|
||||
let severity = code & CompilerErrorSeverity.Severity_Mask;
|
||||
|
|
|
|||
|
|
@ -329,8 +329,7 @@ block(layr) # struct COMP_KMXPLUS_LAYR {
|
|||
01 00 00 00 # KMX_DWORD rowCount
|
||||
02 00 00 00 # KMX_DWORD keyCount
|
||||
# list 0
|
||||
00 00 00 00 # KMX_DWORD flags
|
||||
index(strNull,strUs,2) # KMXPLUS_STR hardware;
|
||||
04 00 00 00 # KMX_DWORD hardware = 'us'
|
||||
00 00 00 00 # KMX_DWORD layer;
|
||||
01 00 00 00 # count
|
||||
7B 00 00 00 # KMX_DWORD minDeviceWidth; // 123
|
||||
|
|
@ -453,7 +452,6 @@ block(strs) # struct COMP_KMXPLUS_STRS {
|
|||
diff(strs,strAuthor) sizeof(strAuthor,2)
|
||||
diff(strs,strConformsTo) sizeof(strConformsTo,2)
|
||||
diff(strs,strThat) sizeof(strThat,2)
|
||||
diff(strs,strUs) sizeof(strUs,2)
|
||||
diff(strs,strTranTo) sizeof(strTranTo,2)
|
||||
diff(strs,strKey1) sizeof(strKey1,2)
|
||||
diff(strs,strKey2) sizeof(strKey2,2)
|
||||
|
|
@ -482,7 +480,6 @@ block(strs) # struct COMP_KMXPLUS_STRS {
|
|||
block(strConformsTo) 74 00 65 00 63 00 68 00 70 00 72 00 65 00 76 00
|
||||
69 00 65 00 77 00 block(x) 00 00 # 'techpreview'
|
||||
block(strThat) 74 00 68 00 61 00 74 00 block(x) 00 00 # 'that'
|
||||
block(strUs) 75 00 73 00 block(x) 00 00
|
||||
block(strTranTo) E2 00 block(x) 00 00 # 'â'
|
||||
block(strKey1) 27 01 block(x) 00 00 # 'ħ'
|
||||
block(strKey2) 90 17 b6 17 block(x) 00 00 # 'ថា'
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<key id="grave" to="🪦" />
|
||||
</keys>
|
||||
|
||||
<layers form="hardware">
|
||||
<layers form="hardware" hardware="us">
|
||||
<layer id="base">
|
||||
<row keys="grave" />
|
||||
</layer>
|
||||
|
|
|
|||
|
|
@ -29,8 +29,7 @@ describe('layr', function () {
|
|||
const list0 = layr.lists[0];
|
||||
assert.ok(list0);
|
||||
assert.equal(list0.layers.length, 1);
|
||||
assert.equal(list0.flags & constants.layr_list_flags_mask_form, constants.layr_list_flags_hardware);
|
||||
assert.equal(list0.hardware?.value, '');
|
||||
assert.equal(list0.hardware, constants.layr_list_hardware_us);
|
||||
const layer0 = list0.layers[0];
|
||||
assert.ok(layer0);
|
||||
assert.equal(layer0.rows.length, 1);
|
||||
|
|
@ -51,12 +50,10 @@ describe('layr', function () {
|
|||
|
||||
assert.equal(layr.lists?.length, 2);
|
||||
|
||||
const listHardware = layr.lists.find(v => v.hardware.value === 'abnt2');
|
||||
const listHardware = layr.lists.find(v => v.hardware === constants.layr_list_hardware_abnt2);
|
||||
assert.ok(listHardware);
|
||||
assert.equal(listHardware.minDeviceWidth, 0);
|
||||
assert.equal(listHardware.layers.length, 2);
|
||||
assert.equal(listHardware.flags & constants.layr_list_flags_mask_form, constants.layr_list_flags_hardware);
|
||||
assert.equal(listHardware.hardware?.value, 'abnt2');
|
||||
const hardware0 = listHardware.layers[0];
|
||||
assert.ok(hardware0);
|
||||
assert.equal(hardware0.id.value, 'base');
|
||||
|
|
@ -75,11 +72,10 @@ describe('layr', function () {
|
|||
assert.equal(hardware1row0.keys.length, 2);
|
||||
allKeysOk(hardware1row0,'q w', 'hardware1row0');
|
||||
|
||||
const listTouch = layr.lists.find(v => v.hardware.value !== 'abnt2'); // TODO-LDML: need to add some more fields!!!
|
||||
const listTouch = layr.lists.find(v => v.hardware === constants.layr_list_hardware_touch);
|
||||
assert.ok(listTouch);
|
||||
assert.equal(listTouch.minDeviceWidth, 300);
|
||||
assert.equal(listTouch.layers.length, 1);
|
||||
assert.equal(listTouch.flags & constants.layr_list_flags_mask_form, constants.layr_list_flags_touch);
|
||||
const touch0 = listTouch.layers[0];
|
||||
assert.ok(touch0);
|
||||
assert.equal(touch0.rows.length, 1);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue