spiegel-keyman/web/source/osk/defaultLayouts.ts

886 lines
No EOL
36 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/***
KeymanWeb 10.0
Copyright 2017 SIL International
***/
///<reference path="../utils/version.ts"/>
namespace com.keyman.osk {
let Codes = com.keyman.text.Codes;
export type KLS = {[layerName: string]: string[]};
// The following types provide type definitions for the full JSON format we use for visual keyboard definitions.
export type ButtonClass = number|"0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"|"10"
export type LayoutKey = {
"id"?: string,
"text"?: string,
"sp"?: ButtonClass,
"width"?: string,
"layer"?: string, // Key derives any modifiers from the value set here if specified, not the actual display layer.
"nextlayer"?: string,
"pad"?: string,
"sk"?: LayoutKey[]
}
export type LayoutRow = {
"id": string, // represents a number, though...
"key": LayoutKey[]
};
export type LayoutLayer = {
"id": string,
"row": LayoutRow[],
// Post-processing elements.
shiftKey?: LayoutKey,
capsKey?: LayoutKey,
numKey?: LayoutKey,
scrollKey?: LayoutKey
aligned?: boolean
}
export type LayoutFormFactor = {
"font": string,
"layer": LayoutLayer[],
keyLabels?: boolean
}
export type LayoutSpec = {
"desktop"?: LayoutFormFactor,
"phone"?: LayoutFormFactor,
"tablet"?: LayoutFormFactor
}
// This class manages default layout construction for consumption by OSKs without a specified layout.
export class Layouts {
static dfltCodes=[
"K_BKQUOTE","K_1","K_2","K_3","K_4","K_5","K_6","K_7","K_8","K_9","K_0",
"K_HYPHEN","K_EQUAL","K_*","K_*","K_*","K_Q","K_W","K_E","K_R","K_T",
"K_Y","K_U","K_I","K_O","K_P","K_LBRKT","K_RBRKT","K_BKSLASH","K_*",
"K_*","K_*","K_A","K_S","K_D","K_F","K_G","K_H","K_J","K_K","K_L",
"K_COLON","K_QUOTE","K_*","K_*","K_*","K_*","K_*","K_oE2",
"K_Z","K_X","K_C","K_V","K_B","K_N","K_M","K_COMMA","K_PERIOD",
"K_SLASH","K_*","K_*","K_*","K_*","K_*","K_SPACE"
];
static dfltText='`1234567890-=\xA7~~qwertyuiop[]\\~~~asdfghjkl;\'~~~~~?zxcvbnm,./~~~~~ '
+'~!@#$%^&*()_+\xA7~~QWERTYUIOP{}\\~~~ASDFGHJKL:"~~~~~?ZXCVBNM<>?~~~~~ ';
// Cross-reference with the ids in osk.setButtonClass.
static buttonClasses: {[name: string]: ButtonClass} = {
'DEFAULT':'0',
'SHIFT':'1',
'SHIFT-ON':'2',
'SPECIAL':'3',
'SPECIAL-ON':'4',
'DEADKEY':'8',
'BLANK':'9',
'HIDDEN':'10'
};
static modifierSpecials = {
'leftalt': '*LAlt*',
'rightalt': '*RAlt*',
'alt': '*Alt*',
'leftctrl': '*LCtrl*',
'rightctrl': '*RCtrl*',
'ctrl': '*Ctrl*',
'ctrl-alt': '*AltGr*',
'leftctrl-leftalt': '*LAltCtrl*',
'rightctrl-rightalt': '*RAltCtrl*',
'leftctrl-leftalt-shift': '*LAltCtrlShift*',
'rightctrl-rightalt-shift': '*RAltCtrlShift*',
'shift': '*Shift*',
'shift-alt': '*AltShift*',
'shift-ctrl': '*CtrlShift*',
'shift-ctrl-alt': '*AltCtrlShift*',
'leftalt-shift': '*LAltShift*',
'rightalt-shift': '*RAltShift*',
'leftctrl-shift': '*LCtrlShift*',
'rightctrl-shift': '*RCtrlShift*'
};
static _BaseLayout: string = 'us'; // default BaseLayout
static _BaseLayoutEuro: {[code: string]: string} = {
'se': '\u00a71234567890+´~~~QWERTYUIOP\u00c5\u00a8\'~~~ASDFGHJKL\u00d6\u00c4~~~~~<ZXCVBNM,.-~~~~~ ', // Swedish
'uk': '`1234567890-=~~~QWERTYUIOP[]#~~~ASDFGHJKL;\'~~~~~\\ZXCVBNM,./~~~~~ ' // UK
}; // I1299 (not currently exposed, but may need to be e.g. for external users)
/**
* Build a default layout for keyboards with no explicit layout
*
* @param {Object} PVK keyboard object (as loaded)
* @param {Object} kbdDevVersion object representing the version of Developer that compiled the keyboard
* @param {number} kbdBitmask keyboard modifier bitmask
* @param {string} formFactor
* @return {Object}
*/
static buildDefaultLayout(PVK, kbdDevVersion: utils.Version, kbdBitmask: number, formFactor: string): LayoutFormFactor {
let keyman = com.keyman.singleton;
let util = keyman.util;
// Build a layout using the default for the device
var layoutType=formFactor;
if(typeof Layouts.dfltLayout[layoutType] != 'object') {
layoutType = 'desktop';
}
// Clone the default layout object for this device
var layout: LayoutFormFactor = util.deepCopy(Layouts.dfltLayout[layoutType]);
var n,layers=layout['layer'], keyLabels: KLS=PVK['KLS'], key102=PVK['K102'];
var i, j, k, m, row, rows: LayoutRow[], key: LayoutKey, keys: LayoutKey[];
var chiral: boolean = (kbdBitmask & Codes.modifierBitmasks.IS_CHIRAL) != 0;
var kmw10Plus = !(typeof keyLabels == 'undefined' || !keyLabels);
if(!kmw10Plus) {
// Save the processed key label information to the keyboard's general data.
// Makes things more efficient elsewhere and for reloading after keyboard swaps.
keyLabels = PVK['KLS'] = Layouts.processLegacyDefinitions(PVK['BK']);
}
// Identify key labels (e.g. *Shift*) that require the special OSK font
var specialLabel=/\*\w+\*/;
// *** Step 1: instantiate the layer objects. ***
// Get the list of valid layers, enforcing that the 'default' layer must be the first one processed.
var validIdList = Object.getOwnPropertyNames(keyLabels), invalidIdList = [];
validIdList.splice(validIdList.indexOf('default'), 1);
validIdList = [ 'default' ].concat(validIdList);
// Automatic AltGr emulation if the 'leftctrl-leftalt' layer is otherwise undefined.
if(Layouts.emulatesAltGr(keyLabels)) {
// We insert only the layers that need to be emulated.
if((validIdList.indexOf('leftctrl-leftalt') == -1) && validIdList.indexOf('rightalt') != -1) {
validIdList.push('leftctrl-leftalt');
keyLabels['leftctrl-leftalt'] = keyLabels['rightalt'];
}
if((validIdList.indexOf('leftctrl-leftalt-shift') == -1) && validIdList.indexOf('rightalt-shift') != -1) {
validIdList.push('leftctrl-leftalt-shift');
keyLabels['leftctrl-leftalt-shift'] = keyLabels['rightalt-shift'];
}
}
// For desktop devices, we must create all layers, even if invalid.
if(formFactor == 'desktop') {
invalidIdList = Layouts.generateLayerIds(chiral);
// Filter out all ids considered valid. (We also don't want duplicates in the following list...)
for(n=0; n<invalidIdList.length; n++) {
if(validIdList.indexOf(invalidIdList[n]) != -1) {
invalidIdList.splice(n--, 1);
}
}
}
// This ensures all 'valid' layers are at the front of the layer array and managed by the main loop below.
// 'invalid' layers aren't handled by the loop and thus remain blank after it.
var idList = validIdList.concat(invalidIdList);
if(kmw10Plus && formFactor != 'desktop') { // KLS exists, so we know the exact layer set.
// Find the SHIFT key...
var shiftKey: LayoutKey = null;
rows = layers[0]['row'];
for(var r=0; r < rows.length; r++) {
keys = rows[r]['key'];
for(var c=0; c < keys.length; c++) {
key = keys[c];
if(key['id'] == 'K_SHIFT') {
shiftKey = key;
}
}
}
if(shiftKey) {
// Erase the legacy shifted subkey array.
shiftKey['sk'] = [];
for(var layerID in keyLabels) {
if(layerID == 'default' || layerID == 'shift') {
// These two are accessible from the layer without subkeys.
continue;
}
// Create a new subkey for the specified layer so that it will be accessible via OSK.
var specialChar = Layouts.modifierSpecials[layerID];
shiftKey['sk'].push(new OSKKeySpec("K_" + specialChar, specialChar, null, "1", layerID));
}
} else {
// Seriously, this should never happen. It's here for the debugging log only.
console.warn("Error in default layout - cannot find default Shift key!");
}
}
for(n=0; n<idList.length; n++) {
// Populate non-default (shifted) keygroups
if(n > 0) {
layers[n]=util.deepCopy(layers[0]);
}
layers[n]['id']=idList[n];
layers[n]['nextlayer']=idList[n]; // This would only be different for a dynamic keyboard
// Extraced into a helper method to improve readability.
Layouts.formatDefaultLayer(layers[n], chiral, formFactor, !!key102);
}
// *** Step 2: Layer objects now exist; time to fill them with the appropriate key labels and key styles ***
for(n=0; n<layers.length; n++) {
var layer=layers[n], kx, shiftKey: LayoutKey = null, nextKey=null, allText='';
var capsKey: LayoutKey = null, numKey: LayoutKey = null, scrollKey: LayoutKey = null; // null if not in the OSK layout.
var layerSpec = keyLabels[layer['id']];
var isShift = layer['id'] == 'shift' ? 1 : 0;
var isDefault = layer['id'] == 'default' || isShift ? 1 : 0;
rows=layer['row'];
for(i=0; i<rows.length; i++) {
keys=rows[i]['key'];
for(j=0; j<keys.length; j++) {
key=keys[j];
kx=Layouts.dfltCodes.indexOf(key['id']);
// Only create keys for defined layers. ('default' and 'shift' are always defined.)
if(layerSpec || isDefault) {
// Get keycap text from visual keyboard array, if defined in keyboard
if(layerSpec) {
if(kx >= 0 && kx < layerSpec.length) key['text']=layerSpec[kx];
}
// Legacy (pre 12.0) behavior: fall back to US English keycap text as default for the base two layers
// if a key cap is not otherwise defined. (Any intentional 'ghost' keys must be explicitly defined.)
if(isDefault && kbdDevVersion.precedes(utils.Version.NO_DEFAULT_KEYCAPS)) {
if(key['id'] != 'K_SPACE' && kx+65 * isShift < Layouts.dfltText.length && key['text'] !== null) {
key['text'] = key['text'] || Layouts.dfltText[kx+65*isShift];
}
}
}
// Leave any unmarked key caps as null strings
if(key['text'] !== null) {
key['text'] = key['text'] || '';
}
// Detect important tracking keys.
switch(key['id']) {
case "K_SHIFT":
shiftKey=key;
break;
case "K_TAB":
nextKey=key;
break;
case "K_CAPS":
capsKey=key;
break;
case "K_NUMLOCK":
numKey=key;
break;
case "K_SCROLL":
scrollKey=key;
break;
}
// Remove pop-up shift keys referencing invalid layers (Build 349)
if(key['sk'] != null) {
for(k=0; k<key['sk'].length; k++) {
if(validIdList.indexOf(key['sk'][k]['nextlayer']) == -1) {
key['sk'].splice(k--, 1);
}
}
if(key['sk'].length == 0) {
key['sk']=null;
}
}
}
}
// We're done with the layer keys initialization pass. Time to do post-analysis layer-level init where necessary.
layer.shiftKey=shiftKey;
layer.capsKey=capsKey;
layer.numKey=numKey;
layer.scrollKey=scrollKey;
// Set modifier key appearance and behaviour for non-desktop devices using the default layout
if(formFactor != 'desktop') {
if(n > 0 && shiftKey != null) {
shiftKey['sp']=Layouts.buttonClasses['SHIFT-ON'];
shiftKey['sk']=null;
shiftKey['text'] = Layouts.modifierSpecials[layers[n].id] ? Layouts.modifierSpecials[layers[n].id] : "*Shift*";
}
}
}
return layout;
}
/**
* Function getLayerId
* Scope Private
* @param {number} m shift modifier code
* @return {string} layer string from shift modifier code (desktop keyboards)
* Description Get name of layer from code, where the modifer order is determined by ascending bit-flag value.
*/
static getLayerId(m: number): string {
let modifierCodes = Codes.modifierCodes;
var s='';
if(m == 0) {
return 'default';
} else {
if(m & modifierCodes['LCTRL']) {
s = (s.length > 0 ? s + '-' : '') + 'leftctrl';
}
if(m & modifierCodes['RCTRL']) {
s = (s.length > 0 ? s + '-' : '') + 'rightctrl';
}
if(m & modifierCodes['LALT']) {
s = (s.length > 0 ? s + '-' : '') + 'leftalt';
}
if(m & modifierCodes['RALT']) {
s = (s.length > 0 ? s + '-' : '') + 'rightalt';
}
if(m & modifierCodes['SHIFT']) {
s = (s.length > 0 ? s + '-' : '') + 'shift';
}
if(m & modifierCodes['CTRL']) {
s = (s.length > 0 ? s + '-' : '') + 'ctrl';
}
if(m & modifierCodes['ALT']) {
s = (s.length > 0 ? s + '-' : '') + 'alt';
}
return s;
}
}
/**
* Signifies whether or not the OSK facilitates AltGr / Right-alt emulation for this keyboard.
* @param {Object=} keyLabels
* @return {boolean}
*/
static emulatesAltGr(keyLabels?: KLS): boolean { // TODO: typing for keyLabels (corresponds to KLS)
var layers;
let keyman = <KeymanBase> window['keyman'];
let modifierCodes = Codes.modifierCodes;
// If we're not chiral, we're not emulating.
if(!keyman.keyboardManager.isChiral()) {
return false;
}
if(!keyLabels) {
var activeKeyboard = keyman.keyboardManager.activeKeyboard;
if(activeKeyboard == null || activeKeyboard['KV'] == null) {
return false;
}
layers = activeKeyboard['KV']['KLS'];
} else {
layers = keyLabels;
}
var emulationMask = modifierCodes['LCTRL'] | modifierCodes['LALT'];
var unshiftedEmulationLayer = layers[Layouts.getLayerId(emulationMask)];
var shiftedEmulationLayer = layers[Layouts.getLayerId(modifierCodes['SHIFT'] | emulationMask)];
// buildDefaultLayout ensures that these are aliased to the original modifier set being emulated.
// As a result, we can directly test for reference equality.
if(unshiftedEmulationLayer != null &&
unshiftedEmulationLayer != layers[Layouts.getLayerId(modifierCodes['RALT'])]) {
return false;
}
if(shiftedEmulationLayer != null &&
shiftedEmulationLayer != layers[Layouts.getLayerId(modifierCodes['RALT'] | modifierCodes['SHIFT'])]) {
return false;
}
// It's technically possible for the OSK to not specify anything while allowing chiral input. A last-ditch catch:
var bitmask = keyman.keyboardManager.getKeyboardModifierBitmask();
if((bitmask & emulationMask) != emulationMask) {
// At least one of the emulation modifiers is never used by the keyboard! We can confirm everything's safe.
return true;
}
if(unshiftedEmulationLayer == null && shiftedEmulationLayer == null) {
// We've run out of things to go on; we can't detect if chiral AltGr emulation is intended or not.
// TODO: handle this again!
// if(!osk.altGrWarning) {
// console.warn("Could not detect if AltGr emulation is safe, but defaulting to active emulation!")
// // Avoid spamming the console with warnings on every call of the method.
// osk.altGrWarning = true;
// }
return true;
}
return true;
}
/**
* Generates a list of potential layer ids for the specified chirality mode.
*
* @param {boolean} chiral // Does the keyboard use chiral modifiers or not?
*/
static generateLayerIds(chiral: boolean): string[] {
var layerCnt, offset;
if(chiral) {
layerCnt=32;
offset=0x01;
} else {
layerCnt=8;
offset=0x10;
}
var layerIds = [];
for(var i=0; i < layerCnt; i++) {
layerIds.push(Layouts.getLayerId(i * offset));
}
return layerIds;
}
/**
* Sets a formatting property for the modifier keys when constructing a default layout for a keyboard.
*
* @param {Object} layer // One layer specification
* @param {boolean} chiral // Whether or not the keyboard uses chiral modifier information.
* @param {string} formFactor // The form factor of the device the layout is being constructed for.
* @param {boolean} key102 // Whether or not the extended key 102 should be hidden.
*/
static formatDefaultLayer(layer: LayoutLayer, chiral: boolean, formFactor: string, key102: boolean) {
var layerId = layer['id'];
let buttonClasses = Layouts.buttonClasses;
// Correct appearance of state-dependent modifier keys according to group
for(var i=0; i<layer['row'].length; i++) {
var row=layer['row'][i];
var keys=row['key'];
for(var j=0; j<keys.length; j++) {
var key=keys[j];
switch(key['id']) {
case 'K_SHIFT':
case 'K_LSHIFT':
case 'K_RSHIFT':
if(layerId.indexOf('shift') != -1) {
key['sp'] = buttonClasses['SHIFT-ON'];
}
if((formFactor != 'desktop') && (layerId != 'default')) {
key['nextlayer']='default';
}
break;
case 'K_LCTRL':
case 'K_LCONTROL':
if(chiral) {
if(layerId.indexOf('leftctrl') != -1) {
key['sp'] = buttonClasses['SHIFT-ON'];
}
break;
}
case 'K_RCTRL':
case 'K_RCONTROL':
if(chiral) {
if(layerId.indexOf('rightctrl') != -1) {
key['sp'] = buttonClasses['SHIFT-ON'];
}
break;
}
case 'K_CONTROL':
if(layerId.indexOf('ctrl') != -1) {
if(!chiral || (layerId.indexOf('leftctrl') != -1 && layerId.indexOf('rightctrl') != -1)) {
key['sp'] = buttonClasses['SHIFT-ON'];
}
}
break;
case 'K_LALT':
if(chiral) {
if(layerId.indexOf('leftalt') != -1) {
key['sp'] = buttonClasses['SHIFT-ON'];
}
break;
}
case 'K_RALT':
if(chiral) {
if(layerId.indexOf('rightalt') != -1) {
key['sp'] = buttonClasses['SHIFT-ON'];
}
break;
}
case 'K_ALT':
if(layerId.indexOf('alt') != -1) {
if(!chiral || (layerId.indexOf('leftalt') != -1 && layerId.indexOf('rightalt') != -1)) {
key['sp'] = buttonClasses['SHIFT-ON'];
}
}
break;
case 'K_oE2':
if(typeof key102 == 'undefined' || !key102) {
if(formFactor == 'desktop') {
keys.splice(j--, 1);
keys[0]['width']='200';
} else {
keys[j]['sp']=buttonClasses['HIDDEN'];
}
}
break;
}
}
}
}
/**
* Converts the legacy BK property from pre 10.0 into the KLS keyboard layer spec format,
* sparsifying it as possible to pre-emptively check invalid layers.
*
* @param {Array} BK keyboard object (as loaded)
* @return {Object}
*/
static processLegacyDefinitions(BK: string[]): KLS {
//['default','shift','ctrl','shiftctrl','alt','shiftalt','ctrlalt','shiftctrlalt'];
var idList=Layouts.generateLayerIds(false); // Non-chiral.
var KLS: KLS = {};
// The old default: eight auto-managed layers...
for(var n=0; n<idList.length; n++) {
var id = idList[n], arr = [], valid = false;
// ... with keycode mappings in blocks of 65.
for(var k=0; k < 65; k++) {
var index = k + 65 * n;
arr.push(BK[index]);
// The entry for K_SPACE's keycode tends to hold ' ' instead of '', which causes
// the whole layer to be treated as 'valid' if not included in the conditional.
if(index < BK.length && BK[index] != '' && k != Layouts.dfltCodes.indexOf('K_SPACE')) {
valid = true;
}
}
if(valid) {
KLS[id] = arr;
}
}
// There must always be at least a plain 'default' layer. Array(65).fill('') would be preferable but isn't supported on IE,
// but buildDefaultLayer will set the defaults for these layers if no entry exists for them in the array due to length.
if(typeof KLS['default'] == 'undefined' || ! KLS['default']) {
KLS['default'] = [''];
}
// There must always be at least a plain 'shift' layer.
if(typeof KLS['shift'] == 'undefined' || ! KLS['shift']) {
KLS['shift'] = [''];
}
return KLS;
}
// Defines the default visual layout for a keyboard.
static dfltLayout: LayoutSpec = {
"desktop":
{
"font": "Tahoma,Helvetica",
"layer": [
{
"id": "default",
"row": [
{
"id": "1",
"key": [
{ "id": "K_BKQUOTE" },
{ "id": "K_1" },
{ "id": "K_2" },
{ "id": "K_3" },
{ "id": "K_4" },
{ "id": "K_5" },
{ "id": "K_6" },
{ "id": "K_7" },
{ "id": "K_8" },
{ "id": "K_9" },
{ "id": "K_0" },
{ "id": "K_HYPHEN" },
{ "id": "K_EQUAL" },
{ "id": "K_BKSP", "text": "*BkSp*", "sp": "1", "width": "130" }
]
},
{
"id": "2",
"key": [
{ "id": "K_TAB", "text": "*Tab*", "sp": "1", "width": "130" },
{ "id": "K_Q" },
{ "id": "K_W" },
{ "id": "K_E" },
{ "id": "K_R" },
{ "id": "K_T" },
{ "id": "K_Y" },
{ "id": "K_U" },
{ "id": "K_I" },
{ "id": "K_O" },
{ "id": "K_P" },
{ "id": "K_LBRKT" },
{ "id": "K_RBRKT" },
{ "id": "K_BKSLASH" }
]
},
{
"id": "3",
"key": [
{ "id": "K_CAPS", "text": "*Caps*", "sp": "1", "width": "165" },
{ "id": "K_A" },
{ "id": "K_S" },
{ "id": "K_D" },
{ "id": "K_F" },
{ "id": "K_G" },
{ "id": "K_H" },
{ "id": "K_J" },
{ "id": "K_K" },
{ "id": "K_L" },
{ "id": "K_COLON" },
{ "id": "K_QUOTE" },
{ "id": "K_ENTER", "text": "*Enter*", "sp": "1", "width": "165" }
]
},
{
"id": "4",
"key": [
{ "id": "K_SHIFT", "text": "*Shift*", "sp": "1", "width": "130" },
{ "id": "K_oE2" },
{ "id": "K_Z" },
{ "id": "K_X" },
{ "id": "K_C" },
{ "id": "K_V" },
{ "id": "K_B" },
{ "id": "K_N" },
{ "id": "K_M" },
{ "id": "K_COMMA" },
{ "id": "K_PERIOD" },
{ "id": "K_SLASH" },
{ "id": "K_RSHIFT", "text": "*Shift*", "sp": "1", "width": "130" }
]
},
{
"id": "5",
"key": [
{ "id": "K_LCONTROL", "text": "*Ctrl*", "sp": "1", "width": "170" },
{ "id": "K_LALT", "text": "*Alt*", "sp": "1", "width": "160" },
{ "id": "K_SPACE", "text": "", "width": "770" },
{ "id": "K_RALT", "text": "*Alt*", "sp": "1", "width": "160" },
{ "id": "K_RCONTROL", "text": "*Ctrl*", "sp": "1", "width": "170" }
]
}
]
}
]
},
"tablet":
{
"font": "Tahoma,Helvetica",
"layer": [
{
"id": "default",
"row": [
{
"id": "0",
"key": [
{ "id": "K_1" },
{ "id": "K_2" },
{ "id": "K_3" },
{ "id": "K_4" },
{ "id": "K_5" },
{ "id": "K_6" },
{ "id": "K_7" },
{ "id": "K_8" },
{ "id": "K_9" },
{ "id": "K_0" },
{ "id": "K_HYPHEN" },
{ "id": "K_EQUAL" },
{ "sp": "10", "width": "1" }
]
},
{
"id": "1",
"key": [
{ "id": "K_Q", "pad": "25" },
{ "id": "K_W" },
{ "id": "K_E" },
{ "id": "K_R" },
{ "id": "K_T" },
{ "id": "K_Y" },
{ "id": "K_U" },
{ "id": "K_I" },
{ "id": "K_O" },
{ "id": "K_P" },
{ "id": "K_LBRKT" },
{ "id": "K_RBRKT" },
{ "sp": "10", "width": "1" }
]
},
{
"id": "2",
"key": [
{ "id": "K_A", "pad": "50" },
{ "id": "K_S" },
{ "id": "K_D" },
{ "id": "K_F" },
{ "id": "K_G" },
{ "id": "K_H" },
{ "id": "K_J" },
{ "id": "K_K" },
{ "id": "K_L" },
{ "id": "K_COLON" },
{ "id": "K_QUOTE" },
{ "id": "K_BKSLASH", "width": "90" }
]
},
{
"id": "3",
"key": [
{ "id": "K_oE2", "width": "90" },
{ "id": "K_Z" },
{ "id": "K_X" },
{ "id": "K_C" },
{ "id": "K_V" },
{ "id": "K_B" },
{ "id": "K_N" },
{ "id": "K_M" },
{ "id": "K_COMMA" },
{ "id": "K_PERIOD" },
{ "id": "K_SLASH" },
{ "id": "K_BKQUOTE" },
{ "sp": "10", "width": "1" }
]
},
{
"id": "4",
"key": [
{
"id": "K_SHIFT", "text": "*Shift*", "sp": "1", "width": "200", "sk": [
{ "id": "K_LCONTROL", "text": "*Ctrl*", "sp": "1", "width": "50", "nextlayer": "ctrl" },
{ "id": "K_LCONTROL", "text": "*LCtrl*", "sp": "1", "width": "50", "nextlayer": "leftctrl" },
{ "id": "K_RCONTROL", "text": "*RCtrl*", "sp": "1", "width": "50", "nextlayer": "rightctrl" },
{ "id": "K_LALT", "text": "*Alt*", "sp": "1", "width": "50", "nextlayer": "alt" },
{ "id": "K_LALT", "text": "*LAlt*", "sp": "1", "width": "50", "nextlayer": "leftalt" },
{ "id": "K_RALT", "text": "*RAlt*", "sp": "1", "width": "50", "nextlayer": "rightalt" },
{ "id": "K_ALTGR", "text": "*AltGr*", "sp": "1", "width": "50", "nextlayer": "ctrl-alt" }]
},
{ "id": "K_LOPT", "text": "*Menu*", "sp": "1", "width": "150" },
{ "id": "K_SPACE", "text": "", "width": "570" },
{ "id": "K_BKSP", "text": "*BkSp*", "sp": "1", "width": "150" },
{ "id": "K_ENTER", "text": "*Enter*", "sp": "1", "width": "200" }
]
}
]
}
]
},
"phone":
{
"font": "Tahoma,Helvetica",
"layer": [
{
"id": "default",
"row": [
{
"id": "0",
"key": [
{ "id": "K_1" },
{ "id": "K_2" },
{ "id": "K_3" },
{ "id": "K_4" },
{ "id": "K_5" },
{ "id": "K_6" },
{ "id": "K_7" },
{ "id": "K_8" },
{ "id": "K_9" },
{ "id": "K_0" },
{ "id": "K_HYPHEN" },
{ "id": "K_EQUAL" },
{ "sp": "10", "width": "1" }
]
},
{
"id": "1",
"key": [
{ "id": "K_Q", "pad": "25" },
{ "id": "K_W" },
{ "id": "K_E" },
{ "id": "K_R" },
{ "id": "K_T" },
{ "id": "K_Y" },
{ "id": "K_U" },
{ "id": "K_I" },
{ "id": "K_O" },
{ "id": "K_P" },
{ "id": "K_LBRKT" },
{ "id": "K_RBRKT" },
{ "sp": "10", "width": "1" }
]
},
{
"id": "2",
"key": [
{ "id": "K_A", "pad": "50" },
{ "id": "K_S" },
{ "id": "K_D" },
{ "id": "K_F" },
{ "id": "K_G" },
{ "id": "K_H" },
{ "id": "K_J" },
{ "id": "K_K" },
{ "id": "K_L" },
{ "id": "K_COLON" },
{ "id": "K_QUOTE" },
{ "id": "K_BKSLASH", "width": "90" }
]
},
{
"id": "3",
"key": [
{ "id": "K_oE2", "width": "90" },
{ "id": "K_Z" },
{ "id": "K_X" },
{ "id": "K_C" },
{ "id": "K_V" },
{ "id": "K_B" },
{ "id": "K_N" },
{ "id": "K_M" },
{ "id": "K_COMMA" },
{ "id": "K_PERIOD" },
{ "id": "K_SLASH" },
{ "id": "K_BKQUOTE" },
{ "sp": "10", "width": "1" }
]
},
{
"id": "4",
"key": [
{
"id": "K_SHIFT", "text": "*Shift*", "sp": "1", "width": "200", "sk": [
{ "id": "K_LCONTROL", "text": "*Ctrl*", "sp": "1", "width": "50", "nextlayer": "ctrl" },
{ "id": "K_LCONTROL", "text": "*LCtrl*", "sp": "1", "width": "50", "nextlayer": "leftctrl" },
{ "id": "K_RCONTROL", "text": "*RCtrl*", "sp": "1", "width": "50", "nextlayer": "rightctrl" },
{ "id": "K_LALT", "text": "*Alt*", "sp": "1", "width": "50", "nextlayer": "alt" },
{ "id": "K_LALT", "text": "*LAlt*", "sp": "1", "width": "50", "nextlayer": "leftalt" },
{ "id": "K_RALT", "text": "*RAlt*", "sp": "1", "width": "50", "nextlayer": "rightalt" },
{ "id": "K_ALTGR", "text": "*AltGr*", "sp": "1", "width": "50", "nextlayer": "ctrl-alt" }]
},
{ "id": "K_LOPT", "text": "*Menu*", "width": "150", "sp": "1" },
{ "id": "K_SPACE", "width": "570", "text": "" },
{ "id": "K_BKSP", "text": "*BkSp*", "width": "150", "sp": "1" },
{ "id": "K_ENTER", "text": "*Enter*", "width": "200", "sp": "1" }
]
}
]
}
]
}
};
}
}