From 3a173c27096b4798be3689f78b3e3cc3a2b5b2f8 Mon Sep 17 00:00:00 2001 From: Balz Guenat Date: Thu, 12 Jan 2017 15:53:33 +0100 Subject: [PATCH 1/5] functionality to map legend position to layer --- src/state/keyboard/key.js | 117 +++++++++++++++++++++++++++++++++++--- 1 file changed, 109 insertions(+), 8 deletions(-) diff --git a/src/state/keyboard/key.js b/src/state/keyboard/key.js index af1924fe..cb71cc83 100644 --- a/src/state/keyboard/key.js +++ b/src/state/keyboard/key.js @@ -130,18 +130,119 @@ class Key { * Guess the legend of the key. */ guessLegend() { - // Get the last legend. + /* + No legends = SPACE on l0 and TRNS on others + Only one legend = assign l0, TRNS on others + No base layer legend but multiple higher layer legends = SPACE on l0, TRNS where unspecified + Others = TRNS where unspecified + + Alternative strict mode: + If legend unspecified = NO on base layer, TRNS elsewhere + + This allows for keys that are inactive on the base layer but SPACE needs to be specified explicitly. + */ + + const strictMode = false; + const legends = this.legend.split('\n'); - const legend = legends[legends.length - 1]; - // Look for an alias. - const keycode = C.KEYCODE_ALIASES[legend.toUpperCase()]; - if (keycode) { - this.keycodes[0] = new Keycode(keycode.template.raw[0], []); + // Simple case with zero or one legends. + if (!strictMode && Key.countTruthy(legends) <= 1) { + const legend = legends[legends.length - 1]; + this.keycodes[0] = Key.legendToKeycode(legend, 'KC_NO'); + return; + } + + // See https://github.com/ijprest/keyboard-layout-editor/wiki/Serialized-Data-Format + const positionToIndex = { + "top left": 0, + "bottom left": 1, + "top right": 2, + "bottom right": 3, + "front left": 4, + "front right": 5, + "center left": 6, + "center right": 7, + "top center": 8, + "center": 9, + "bottom center": 10, + "front center": 11 + }; + + /* + The following maps the positions to the following layers: + + left center right + top 1 + center 0 + bottom 2 + front + + TODO: Make this configurable by the user. + */ + const indexToLayer = []; + indexToLayer[positionToIndex["center"]] = 0; + indexToLayer[positionToIndex["top right"]] = 1; + indexToLayer[positionToIndex["bottom left"]] = 2; + + // layers has the same elements as legends but in such an order that the index of a legend is equal to the + // layer it is to be assigned to. + const layers = []; + for (let i = 0; i < legends.length; i++) { + const layer = indexToLayer[i]; + if (layer) + layers[layer] = legends[i]; + } + + // base layer + const legend = layers[0]; + if (legend) { + this.keycodes[0] = Key.legendToKeycode(legend, 'KC_NO'); } else { - // Default to KC_NO. - this.keycodes[0] = new Keycode('KC_NO', []); + if (strictMode) + this.keycodes[0] = new Keycode('KC_NO', []); + else { + // Assign KC_SPACE if blank. + this.keycodes[0] = new Keycode('KC_SPACE', []); + } } + + // higher layers + for (let i = 1; i < layers.length; i++) { + const legend = layers[i]; + if (legend) { + this.keycodes[i] = Key.legendToKeycode(legend, 'KC_TRNS'); + } + } + } + + /** + * Counts the number of truthy values in arr. + * @param {Array} arr The array to count. + * @returns {Number} The number of truthy values in arr. + */ + static countTruthy(arr) { + return arr.reduce(function(count, value) { + if (value) + return count + 1; + else + return count; + }, 0); + } + + /** + * Returns a new keycode object corresponding to legend, or if no such keycode can be found, one corresponsing to + * fallback. + * + * @param {String} legend The legend to which a keycode should be found. + * @param {String} fallback A string specifing the fallback keycode (e.g. "KC_NO" or "KC_TRNS"). + */ + static legendToKeycode(legend, fallback) { + const keycode = C.KEYCODE_ALIASES[legend.toUpperCase()]; + if (keycode) + return new Keycode(keycode.template.raw[0], []); + else + return new Keycode(fallback, []); } /* From 0408aa1685ebe168a11d163b47081467b164e014 Mon Sep 17 00:00:00 2001 From: Balz Guenat Date: Thu, 12 Jan 2017 18:39:46 +0100 Subject: [PATCH 2/5] two small fixes --- src/state/keyboard/key.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/state/keyboard/key.js b/src/state/keyboard/key.js index cb71cc83..6ae836d5 100644 --- a/src/state/keyboard/key.js +++ b/src/state/keyboard/key.js @@ -190,7 +190,7 @@ class Key { const layers = []; for (let i = 0; i < legends.length; i++) { const layer = indexToLayer[i]; - if (layer) + if (layer != undefined) layers[layer] = legends[i]; } @@ -202,8 +202,8 @@ class Key { if (strictMode) this.keycodes[0] = new Keycode('KC_NO', []); else { - // Assign KC_SPACE if blank. - this.keycodes[0] = new Keycode('KC_SPACE', []); + // Assign SPACE if blank. + this.keycodes[0] = new Keycode('KC_SPC', []); } } From 447def5eb4084c80c574289a8bcd089c1a54a3ca Mon Sep 17 00:00:00 2001 From: Balz Guenat Date: Sat, 14 Jan 2017 13:23:43 +0100 Subject: [PATCH 3/5] Code style changes. --- src/state/keyboard/key.js | 69 ++++++++++++++++----------------------- src/utils.js | 15 +++++++++ 2 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/state/keyboard/key.js b/src/state/keyboard/key.js index 6ae836d5..5c576651 100644 --- a/src/state/keyboard/key.js +++ b/src/state/keyboard/key.js @@ -147,26 +147,26 @@ class Key { const legends = this.legend.split('\n'); // Simple case with zero or one legends. - if (!strictMode && Key.countTruthy(legends) <= 1) { + if (!strictMode && Utils.countTruthy(legends) <= 1) { const legend = legends[legends.length - 1]; this.keycodes[0] = Key.legendToKeycode(legend, 'KC_NO'); return; } - // See https://github.com/ijprest/keyboard-layout-editor/wiki/Serialized-Data-Format + // See https://github.com/ijprest/keyboard-layout-editor/wiki/Serialized-Data-Format. const positionToIndex = { - "top left": 0, - "bottom left": 1, - "top right": 2, - "bottom right": 3, - "front left": 4, - "front right": 5, - "center left": 6, - "center right": 7, - "top center": 8, - "center": 9, - "bottom center": 10, - "front center": 11 + 'top left': 0, + 'bottom left': 1, + 'top right': 2, + 'bottom right': 3, + 'front left': 4, + 'front right': 5, + 'center left': 6, + 'center right': 7, + 'top center': 8, + 'center': 9, + 'bottom center': 10, + 'front center': 11 }; /* @@ -177,37 +177,36 @@ class Key { center 0 bottom 2 front - - TODO: Make this configurable by the user. */ const indexToLayer = []; - indexToLayer[positionToIndex["center"]] = 0; - indexToLayer[positionToIndex["top right"]] = 1; - indexToLayer[positionToIndex["bottom left"]] = 2; + indexToLayer[positionToIndex['center']] = 0; + indexToLayer[positionToIndex['top right']] = 1; + indexToLayer[positionToIndex['bottom left']] = 2; // layers has the same elements as legends but in such an order that the index of a legend is equal to the // layer it is to be assigned to. const layers = []; for (let i = 0; i < legends.length; i++) { const layer = indexToLayer[i]; - if (layer != undefined) + if (layer != undefined) { layers[layer] = legends[i]; + } } - // base layer + // Base layer, using 'KC_NO' as a fallback. const legend = layers[0]; if (legend) { this.keycodes[0] = Key.legendToKeycode(legend, 'KC_NO'); } else { - if (strictMode) + if (strictMode) { this.keycodes[0] = new Keycode('KC_NO', []); - else { + } else { // Assign SPACE if blank. this.keycodes[0] = new Keycode('KC_SPC', []); } } - // higher layers + // Higher layers, using 'KC_TRNS' as a fallback. for (let i = 1; i < layers.length; i++) { const legend = layers[i]; if (legend) { @@ -216,33 +215,21 @@ class Key { } } - /** - * Counts the number of truthy values in arr. - * @param {Array} arr The array to count. - * @returns {Number} The number of truthy values in arr. - */ - static countTruthy(arr) { - return arr.reduce(function(count, value) { - if (value) - return count + 1; - else - return count; - }, 0); - } - /** * Returns a new keycode object corresponding to legend, or if no such keycode can be found, one corresponsing to * fallback. * * @param {String} legend The legend to which a keycode should be found. - * @param {String} fallback A string specifing the fallback keycode (e.g. "KC_NO" or "KC_TRNS"). + * @param {String} fallback A string specifing the fallback keycode (e.g. 'KC_NO' or 'KC_TRNS'). + * @return {Keycode} The newly constructed keycode. */ static legendToKeycode(legend, fallback) { const keycode = C.KEYCODE_ALIASES[legend.toUpperCase()]; - if (keycode) + if (keycode) { return new Keycode(keycode.template.raw[0], []); - else + } else { return new Keycode(fallback, []); + } } /* diff --git a/src/utils.js b/src/utils.js index 49f96f4f..fcb5f4d5 100644 --- a/src/utils.js +++ b/src/utils.js @@ -107,6 +107,21 @@ class Utils { return input; } + /** + * Counts the number of truthy values in arr. + * @param {Array} arr The array to count. + * @return {Number} The number of truthy values in arr. + */ + static countTruthy(arr) { + return arr.reduce((count, value) => { + if (value) { + return count + 1; + } else { + return count; + } + }, 0); + } + } module.exports = Utils; From ce5b5f2b245009b2e3f6e979a1604761a9b7e8e4 Mon Sep 17 00:00:00 2001 From: Balz Guenat Date: Sat, 14 Jan 2017 17:21:27 +0100 Subject: [PATCH 4/5] UI to configure layer mapping --- src/const/index.js | 16 +++++ src/state/keyboard/index.js | 39 ++++++++++- src/state/keyboard/key.js | 43 +++--------- src/ui/elements/layerbox/index.js | 51 ++++++++++++++ src/ui/panes/settings/index.js | 110 ++++++++++++++++++++++++++++++ 5 files changed, 224 insertions(+), 35 deletions(-) create mode 100644 src/ui/elements/layerbox/index.js diff --git a/src/const/index.js b/src/const/index.js index 6ad88ae1..0439125b 100644 --- a/src/const/index.js +++ b/src/const/index.js @@ -72,6 +72,22 @@ const C = { // Keymap. KEYMAP_MAX_LAYERS: 16, + // KLE legend position indices (see https://github.com/ijprest/keyboard-layout-editor/wiki/Serialized-Data-Format). + POSITION_TO_INDEX: { + 'top left': 0, + 'bottom left': 1, + 'top right': 2, + 'bottom right': 3, + 'front left': 4, + 'front right': 5, + 'center left': 6, + 'center right': 7, + 'top center': 8, + 'center': 9, + 'bottom center': 10, + 'front center': 11 + }, + // Macros. MACRO_NONE: 0, MACRO_INTERVAL: 1, diff --git a/src/state/keyboard/index.js b/src/state/keyboard/index.js index 6bda1a6a..533f3abe 100644 --- a/src/state/keyboard/index.js +++ b/src/state/keyboard/index.js @@ -40,7 +40,9 @@ class Keyboard { name: '', bootloaderSize: C.BOOTLOADER_4096, rgbNum: 0, - backlightLevels: 3 + backlightLevels: 3, + positionIndexToLayerMap: [], + strictLayers: false }; this.valid = false; @@ -65,6 +67,10 @@ class Keyboard { this.deselect = this.deselect.bind(this); + this.setLayer = this.setLayer.bind(this); + this.getLayer = this.getLayer.bind(this); + this.updateLayers = this.updateLayers.bind(this); + // Import KLE if it exists. if (json) this.importKLE(json); @@ -409,6 +415,37 @@ class Keyboard { this.state.update(); } + getLayer(position) { + const positionIndex = C.POSITION_TO_INDEX[position]; + return this.settings.positionIndexToLayerMap[positionIndex]; + } + + /** + * Maps the supplied legend position to the supplied layer. + * + * @param {String} position The legend position to be mapped. + * @param {Number} layer The layer the position should be mapped to. + */ + setLayer(position, layer) { + const positionIndex = C.POSITION_TO_INDEX[position]; + if (isNaN(layer)) { + this.settings.positionIndexToLayerMap[positionIndex] = undefined; + } else { + this.settings.positionIndexToLayerMap[positionIndex] = layer; + } + + this.state.update(); + } + + updateLayers() { + for (const key of this.keys) { + key.guessLegend(); + } + this.verify(); + + this.state.update(); + } + /* * Deselect all keys. */ diff --git a/src/state/keyboard/key.js b/src/state/keyboard/key.js index 5c576651..6eeab2ee 100644 --- a/src/state/keyboard/key.js +++ b/src/state/keyboard/key.js @@ -22,7 +22,7 @@ class Key { this.selected = 0; - this.keycodes = Array(C.KEYMAP_MAX_LAYERS).fill(new Keycode('KC_TRNS', [])); + this.keycodes = new Array(C.KEYMAP_MAX_LAYERS); // Bind functions. this.guessLegend = this.guessLegend.bind(this); @@ -142,47 +142,22 @@ class Key { This allows for keys that are inactive on the base layer but SPACE needs to be specified explicitly. */ - const strictMode = false; + this.keycodes.fill(new Keycode('KC_TRNS', [])); + + const strictMode = this.keyboard.settings.strictLayers; const legends = this.legend.split('\n'); - // Simple case with zero or one legends. - if (!strictMode && Utils.countTruthy(legends) <= 1) { + const indexToLayer = this.keyboard.settings.positionIndexToLayerMap; + const isLayerMapDefined = indexToLayer.some(v => { return v != undefined }); + + // Simple case with zero or one legends or default behavior with no layer map. + if (!isLayerMapDefined || Utils.countTruthy(legends) <= 1) { const legend = legends[legends.length - 1]; this.keycodes[0] = Key.legendToKeycode(legend, 'KC_NO'); return; } - // See https://github.com/ijprest/keyboard-layout-editor/wiki/Serialized-Data-Format. - const positionToIndex = { - 'top left': 0, - 'bottom left': 1, - 'top right': 2, - 'bottom right': 3, - 'front left': 4, - 'front right': 5, - 'center left': 6, - 'center right': 7, - 'top center': 8, - 'center': 9, - 'bottom center': 10, - 'front center': 11 - }; - - /* - The following maps the positions to the following layers: - - left center right - top 1 - center 0 - bottom 2 - front - */ - const indexToLayer = []; - indexToLayer[positionToIndex['center']] = 0; - indexToLayer[positionToIndex['top right']] = 1; - indexToLayer[positionToIndex['bottom left']] = 2; - // layers has the same elements as legends but in such an order that the index of a legend is equal to the // layer it is to be assigned to. const layers = []; diff --git a/src/ui/elements/layerbox/index.js b/src/ui/elements/layerbox/index.js new file mode 100644 index 00000000..dba2e377 --- /dev/null +++ b/src/ui/elements/layerbox/index.js @@ -0,0 +1,51 @@ +const React = require('react'); + +class LayerBox extends React.Component { + + constructor(props) { + super(props); + + // Bind functions. + this.onChange = this.onChange.bind(this); + this.changeValue = this.changeValue.bind(this); + } + + /* + * Called when the value of the input changes. + * + * @param {Event} e The event triggered. + */ + onChange(e) { + const target = e.target; + let value = target.value.trim(); + + // Change the value. + this.changeValue(parseInt(value)); + } + + /* + * Called to change the value to a certain value. + * + * @param {Number} value The value to change to. + */ + changeValue(value) { + // Make sure there is a function we can call. + if (!this.props.onChange) return; + + // Change the value. + this.props.onChange(value); + } + + render() { + return
+ +
; + } + +} + +module.exports = LayerBox; diff --git a/src/ui/panes/settings/index.js b/src/ui/panes/settings/index.js index fb7ea481..e0dac42f 100644 --- a/src/ui/panes/settings/index.js +++ b/src/ui/panes/settings/index.js @@ -1,6 +1,7 @@ const React = require('react'); const NumberBox = require('ui/elements/numberbox'); +const LayerBox = require('ui/elements/layerbox'); const Help = require('ui/elements/help'); const C = require('const'); @@ -130,6 +131,115 @@ class Settings extends React.Component { The number of backlight levels.
+

Legend-to-layer mapping

+ + Select which layer the legends in each position should be mapped to. Only affects keys with multiple + legends. + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 leftcenterright
top + keyboard.setLayer('top left', v) }/> + + keyboard.setLayer('top center', v) }/> + + keyboard.setLayer('top right', v) }/> +
center + keyboard.setLayer('center left', v) }/> + + keyboard.setLayer('center', v) }/> + + keyboard.setLayer('center right', v) }/> +
bottom + keyboard.setLayer('bottom left', v) }/> + + keyboard.setLayer('bottom center', v) }/> + + keyboard.setLayer('bottom right', v) }/> +
front + keyboard.setLayer('front left', v) }/> + + keyboard.setLayer('front center', v) }/> + + keyboard.setLayer('front right', v) }/> +
+
+ + + + In strict mode, 'KC_NO' is assigned to keys with no legend for the base layer. In strict mode, the space + bar needs to be specified explicitly by adding the legend 'Space'. + +
+ + + (resets manual changes) + +
Save your layout.
- - (resets manual changes) -
Save your layout.