Skip to content

Commit

Permalink
feat(developer): cleanup touch layout files on load and save
Browse files Browse the repository at this point in the history
Improves the .keyman-touch-layout clean schema and adds additional
cleanup during load and save in the Touch Layout Editor.

The compiler already does transforms for the relevant fields to the
formats that KeymanWeb is expecting (in particular, width, pad to
string).

This is preparation for adding hint data to the format as well.
  • Loading branch information
mcdurdin committed Jul 14, 2022
1 parent 60dabb5 commit 51a94a1
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 33 deletions.
17 changes: 10 additions & 7 deletions common/schemas/keyman-touch-layout/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ The general structure of a .keyman-touch-layout file is:
## `root` object

The `root` object may contain up to three properties, each being a [`platform`
object](#platform-object), named `"phone"`, `"tablet"` or `"desktop"`.
object](#platform-object), named `"phone"`, `"tablet"` or `"desktop"`. There
must be at least one property.

## `platform` object

Expand All @@ -68,7 +69,7 @@ optionally contain three additional properties: `font`, `fontsize`, and
`displayUnderlying`.

* `layer`
: An array of [`layer` objects](#layer-object). Required.
: A non-empty array of [`layer` objects](#layer-object). Required.

* `font`
: An optional CSS-style font-family specification, being the name of the font to
Expand Down Expand Up @@ -140,7 +141,7 @@ affect the modifiers in any key events generated by keys on the layer.

* `row`

: An array of [`row` objects](#row-object). Required.
: A non-empty array of [`row` objects](#row-object). Required.

## `row` object

Expand All @@ -150,19 +151,19 @@ properties: `id` and `key`.
* `id`

: An non-negative integer denoting where the row appears, with `0` being the top
row.
row. Required.

Legacy: this value was sometimes an integer in a string. This string format is
deprecated and will not be supported in future versions of Keyman Developer.

* `key`

: An array of [`key` objects](#key-object).
: A non-empty array of [`key` objects](#key-object). Required.

## `key` object

Describes a single key on a touch layout row. See also [`sk`
object](#sk-object). A `key` object has no required properties.
object](#sk-object). A `key` object must include either an `id` or `sp` property.

* `id`

Expand Down Expand Up @@ -314,7 +315,9 @@ properties are not fully supported as of Keyman 15: `font`, `fontsize`, `pad`,
and `width`.

* `id`
: A string identifier for the longpress key. See `key` object for details.

: String. Required. An identifier for the longpress key. See `key` object for
details.

* `text`

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
"tablet": { "$ref": "#/definitions/platform" },
"phone": { "$ref": "#/definitions/platform" },
"desktop": { "$ref": "#/definitions/platform" }
}
},
"minProperties": 1,
"additionalProperties": false
},

"platform": {
Expand All @@ -28,7 +30,8 @@

"layers": {
"type": "array",
"items": { "$ref": "#/definitions/layer" }
"items": { "$ref": "#/definitions/layer" },
"minItems": 1
},

"layer": {
Expand All @@ -48,7 +51,8 @@

"rows": {
"type": "array",
"items": { "$ref": "#/definitions/row" }
"items": { "$ref": "#/definitions/row" },
"minItems": 1
},

"row": {
Expand All @@ -69,7 +73,8 @@

"keys": {
"type": "array",
"items": { "$ref": "#/definitions/key" }
"items": { "$ref": "#/definitions/key" },
"minItems": 1
},

"key": {
Expand All @@ -88,6 +93,13 @@
"flick": { "$ref": "#/definitions/flick" },
"multitap": { "$ref": "#/definitions/subkeys" }
},
"anyOf": [
{"required": ["id"]},
{"required": ["sp"]},
{"required": ["sk"]},
{"required": ["flick"]},
{"required": ["multitap"]}
],
"additionalProperties": false
},

Expand All @@ -98,24 +110,25 @@

"key-sp": {
"type": "integer",
"enum": [0, 1, 2, 8, 9, 10],
"enum": [0, 1, 2, 8, 9, 10]
},

"key-pad": {
"type": "number",
"minimum": 0,
"maximum": 10000
"maximum": 100000
},

"key-width": {
"type": "number",
"minimum": 0,
"maximum": 1000
"maximum": 100000
},

"subkeys": {
"type": "array",
"items": { "$ref": "#/definitions/subkey" }
"items": { "$ref": "#/definitions/subkey" },
"minItems": 1
},

"subkey": {
Expand All @@ -140,6 +153,7 @@
"patternProperties": {
"^(n|s|e|w|ne|nw|se|sw)$": { "$ref": "#/definitions/subkey" }
},
"minProperties": 1,
"additionalProperties": false
},

Expand Down
20 changes: 2 additions & 18 deletions developer/src/tike/xml/layoutbuilder/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,7 @@ $(function() {
};

this.saveJSON = function(force) {
builder.removeEmptyRows();
builder.cleanupKVKL();
var newJson = JSON.stringify(KVKL, null, ' ');
if(force || newJson != json) {
// When adding or deleting layers and platforms, we need to force because
Expand Down Expand Up @@ -978,23 +978,6 @@ $(function() {
);
};

builder.removeEmptyRows = function() {
var json = JSON.stringify(KVKL, null, ' ');
for (var platform in KVKL) {
for (let layer of KVKL[platform].layer) {
let newRow = [], n = 1;
for(let row of layer.row) {
if(row.key.length > 0) {
row.id = n;
n++;
newRow.push(row);
}
}
layer.row = newRow;
}
}
};

builder.textControlsInToolbar = function() {
return $('body').hasClass('text-controls-in-toolbar');
}
Expand All @@ -1004,6 +987,7 @@ $(function() {

function initBuilder() {
$(function() {
builder.cleanupKVKL();
builder.prepareKeyCapTypes();
builder.preparePlatforms();
builder.enableUndoControls();
Expand Down
1 change: 1 addition & 0 deletions developer/src/tike/xml/layoutbuilder/builder.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<script><xsl:attribute name="src"><xsl:value-of select="/TouchLayoutBuilder/LibPath"/>layer-controls.js</xsl:attribute></script>
<script><xsl:attribute name="src"><xsl:value-of select="/TouchLayoutBuilder/LibPath"/>builder-charmap.js</xsl:attribute></script>
<script><xsl:attribute name="src"><xsl:value-of select="/TouchLayoutBuilder/LibPath"/>drag-drop.js</xsl:attribute></script>
<script><xsl:attribute name="src"><xsl:value-of select="/TouchLayoutBuilder/LibPath"/>cleanup.js</xsl:attribute></script>
<script>initBuilder();</script>
</head>
<body class='text-controls-in-toolbar'>
Expand Down
79 changes: 79 additions & 0 deletions developer/src/tike/xml/layoutbuilder/cleanup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
$(function() {

builder.cleanupKVKL = function() {
let cleanupEmptyArrays = function() {
for (let platform in KVKL) {
for (let layer of KVKL[platform].layer) {
let newRow = [], n = 1;
for(let row of layer.row) {
if(row.key && Array.isArray(row.key)) {
for(let key of row.key) {
if(key.sk && (!Array.isArray(key.sk) || key.sk.length == 0)) {
delete key.sk;
}
}
if(row.key.length > 0) {
row.id = n;
n++;
newRow.push(row);
}
}
}
layer.row = newRow;
}
}
return true;
};

let cleanupNumericField = function(obj, prop) {
if(typeof obj[prop] == 'number') {
return true;
}
if(typeof obj[prop] == 'string') {
let v = obj[prop].trim();
if(v != '') {
v = parseInt(v, 10);
if(isFinite(v)) {
obj[prop] = v;
return true;
}
}
}
delete obj[prop];
return true;
}

let cleanupKeyTypes = function(key) {
// Delete empty strings for id
if(typeof key.id != 'undefined') {
if(typeof key.id != 'string') delete key.id;
else if(key.id.trim() == '') delete key.id;
}
// Enforce numeric types for sp, pad, width
return cleanupNumericField(key, 'sp') &&
cleanupNumericField(key, 'pad') &&
cleanupNumericField(key, 'width');
}

let cleanupTypes = function() {
for(let platform in KVKL) {
for(let layer of KVKL[platform].layer) {
for(let row of layer.row) {
for(let key of row.key) {
cleanupKeyTypes(key);
if(key.sk) {
for(let sk of key.sk) {
cleanupKeyTypes(sk);
}
}
}
}
}
}
return true;
}

return cleanupEmptyArrays() && cleanupTypes();
};

}.bind(builder));

0 comments on commit 51a94a1

Please sign in to comment.