diff options
| author | Joel Challis <git@zvecr.com> | 2023-11-20 15:41:48 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-11-20 15:41:48 +0000 |
| commit | e279c78ba3054ebc2e294dd91ea98341cc509d1e (patch) | |
| tree | f22fc30fa1f966c708ec137f667c44a6528c4403 | |
| parent | 62cca5c43a63e694da4596dc41d1251f9c530198 (diff) | |
Enable linking of encoders to switch within layout macros (#22264)
| -rw-r--r-- | data/schemas/keyboard.jsonschema | 1 | ||||
| -rw-r--r-- | docs/reference_info_json.md | 2 | ||||
| -rw-r--r-- | keyboards/drop/sense75/info.json | 2 | ||||
| -rw-r--r-- | lib/python/qmk/info.py | 28 | ||||
| -rw-r--r-- | lib/python/qmk/keyboard.py | 55 |
5 files changed, 86 insertions, 2 deletions
diff --git a/data/schemas/keyboard.jsonschema b/data/schemas/keyboard.jsonschema index 9fc455530c..2996958084 100644 --- a/data/schemas/keyboard.jsonschema +++ b/data/schemas/keyboard.jsonschema | |||
| @@ -348,6 +348,7 @@ | |||
| 348 | "additionalProperties": false, | 348 | "additionalProperties": false, |
| 349 | "required": ["x", "y"], | 349 | "required": ["x", "y"], |
| 350 | "properties": { | 350 | "properties": { |
| 351 | "encoder": {"$ref": "qmk.definitions.v1#/unsigned_int"}, | ||
| 351 | "label": { | 352 | "label": { |
| 352 | "type": "string", | 353 | "type": "string", |
| 353 | "pattern": "^[^\\n]*$" | 354 | "pattern": "^[^\\n]*$" |
diff --git a/docs/reference_info_json.md b/docs/reference_info_json.md index 4a70a4bb6f..e102b9bfb9 100644 --- a/docs/reference_info_json.md +++ b/docs/reference_info_json.md | |||
| @@ -324,6 +324,8 @@ The ISO enter key is represented by a 1.25u×2uh key. Renderers which utilize in | |||
| 324 | * `w` | 324 | * `w` |
| 325 | * The width of the key, in key units. | 325 | * The width of the key, in key units. |
| 326 | * Default: `1` (1u) | 326 | * Default: `1` (1u) |
| 327 | * `encoder` | ||
| 328 | * The index of an encoder this key should be linked to | ||
| 327 | * Example: `{"label": "Shift", "matrix": [4, 0], "x": 0, "y": 4.25, "w": 2.25}` | 329 | * Example: `{"label": "Shift", "matrix": [4, 0], "x": 0, "y": 4.25, "w": 2.25}` |
| 328 | 330 | ||
| 329 | ## Leader Key :id=leader-key | 331 | ## Leader Key :id=leader-key |
diff --git a/keyboards/drop/sense75/info.json b/keyboards/drop/sense75/info.json index dbefc108f2..052b494375 100644 --- a/keyboards/drop/sense75/info.json +++ b/keyboards/drop/sense75/info.json | |||
| @@ -44,7 +44,7 @@ | |||
| 44 | {"matrix": [0, 11], "label": "F11", "x": 11.75, "y": 0}, | 44 | {"matrix": [0, 11], "label": "F11", "x": 11.75, "y": 0}, |
| 45 | {"matrix": [0, 12], "label": "F12", "x": 12.75, "y": 0}, | 45 | {"matrix": [0, 12], "label": "F12", "x": 12.75, "y": 0}, |
| 46 | {"matrix": [0, 13], "label": "PrtSc", "x": 14, "y": 0}, | 46 | {"matrix": [0, 13], "label": "PrtSc", "x": 14, "y": 0}, |
| 47 | {"matrix": [0, 14], "label": "Mute", "x": 15.25, "y": 0}, | 47 | {"matrix": [0, 14], "encoder":0, "label": "Mute", "x": 15.25, "y": 0}, |
| 48 | {"matrix": [1, 0], "label": "~", "x": 0, "y": 1.25}, | 48 | {"matrix": [1, 0], "label": "~", "x": 0, "y": 1.25}, |
| 49 | {"matrix": [1, 1], "label": "!", "x": 1, "y": 1.25}, | 49 | {"matrix": [1, 1], "label": "!", "x": 1, "y": 1.25}, |
| 50 | {"matrix": [1, 2], "label": "@", "x": 2, "y": 1.25}, | 50 | {"matrix": [1, 2], "label": "@", "x": 2, "y": 1.25}, |
diff --git a/lib/python/qmk/info.py b/lib/python/qmk/info.py index fe829a724a..3efd34555c 100644 --- a/lib/python/qmk/info.py +++ b/lib/python/qmk/info.py | |||
| @@ -55,6 +55,29 @@ def _get_key_left_position(key): | |||
| 55 | return key['x'] - 0.25 if key.get('h', 1) == 2 and key.get('w', 1) == 1.25 else key['x'] | 55 | return key['x'] - 0.25 if key.get('h', 1) == 2 and key.get('w', 1) == 1.25 else key['x'] |
| 56 | 56 | ||
| 57 | 57 | ||
| 58 | def _find_invalid_encoder_index(info_data): | ||
| 59 | """Perform additional validation of encoders | ||
| 60 | """ | ||
| 61 | enc_count = len(info_data.get('encoder', {}).get('rotary', [])) | ||
| 62 | enc_count += len(info_data.get('split', {}).get('encoder', {}).get('right', {}).get('rotary', [])) | ||
| 63 | |||
| 64 | ret = [] | ||
| 65 | layouts = info_data.get('layouts', {}) | ||
| 66 | for layout_name, layout_data in layouts.items(): | ||
| 67 | found = set() | ||
| 68 | for key in layout_data['layout']: | ||
| 69 | if 'encoder' in key: | ||
| 70 | if enc_count == 0: | ||
| 71 | ret.append((layout_name, key['encoder'], 'non-configured')) | ||
| 72 | elif key['encoder'] >= enc_count: | ||
| 73 | ret.append((layout_name, key['encoder'], 'out of bounds')) | ||
| 74 | elif key['encoder'] in found: | ||
| 75 | ret.append((layout_name, key['encoder'], 'duplicate')) | ||
| 76 | found.add(key['encoder']) | ||
| 77 | |||
| 78 | return ret | ||
| 79 | |||
| 80 | |||
| 58 | def _additional_validation(keyboard, info_data): | 81 | def _additional_validation(keyboard, info_data): |
| 59 | """Non schema checks | 82 | """Non schema checks |
| 60 | """ | 83 | """ |
| @@ -105,6 +128,11 @@ def _additional_validation(keyboard, info_data): | |||
| 105 | if not decl.get("aliases", []): | 128 | if not decl.get("aliases", []): |
| 106 | _log_error(info_data, f'Keycode {decl["key"]} has no short form alias') | 129 | _log_error(info_data, f'Keycode {decl["key"]} has no short form alias') |
| 107 | 130 | ||
| 131 | # encoder IDs in layouts must be in range and not duplicated | ||
| 132 | found = _find_invalid_encoder_index(info_data) | ||
| 133 | for layout_name, encoder_index, reason in found: | ||
| 134 | _log_error(info_data, f'Layout "{layout_name}" contains {reason} encoder index {encoder_index}.') | ||
| 135 | |||
| 108 | 136 | ||
| 109 | def _validate(keyboard, info_data): | 137 | def _validate(keyboard, info_data): |
| 110 | """Perform various validation on the provided info.json data | 138 | """Perform various validation on the provided info.json data |
diff --git a/lib/python/qmk/keyboard.py b/lib/python/qmk/keyboard.py index 1aa63687d4..4e525731f7 100644 --- a/lib/python/qmk/keyboard.py +++ b/lib/python/qmk/keyboard.py | |||
| @@ -30,6 +30,28 @@ BOX_DRAWING_CHARACTERS = { | |||
| 30 | "h": "_", | 30 | "h": "_", |
| 31 | }, | 31 | }, |
| 32 | } | 32 | } |
| 33 | ENC_DRAWING_CHARACTERS = { | ||
| 34 | "unicode": { | ||
| 35 | "tl": "╭", | ||
| 36 | "tr": "╮", | ||
| 37 | "bl": "╰", | ||
| 38 | "br": "╯", | ||
| 39 | "vl": "▲", | ||
| 40 | "vr": "▼", | ||
| 41 | "v": "│", | ||
| 42 | "h": "─", | ||
| 43 | }, | ||
| 44 | "ascii": { | ||
| 45 | "tl": " ", | ||
| 46 | "tr": " ", | ||
| 47 | "bl": "\\", | ||
| 48 | "br": "/", | ||
| 49 | "v": "|", | ||
| 50 | "vl": "/", | ||
| 51 | "vr": "\\", | ||
| 52 | "h": "_", | ||
| 53 | }, | ||
| 54 | } | ||
| 33 | 55 | ||
| 34 | 56 | ||
| 35 | class AllKeyboards: | 57 | class AllKeyboards: |
| @@ -213,7 +235,9 @@ def render_layout(layout_data, render_ascii, key_labels=None): | |||
| 213 | else: | 235 | else: |
| 214 | label = key.get('label', '') | 236 | label = key.get('label', '') |
| 215 | 237 | ||
| 216 | if x >= 0.25 and w == 1.25 and h == 2: | 238 | if 'encoder' in key: |
| 239 | render_encoder(textpad, x, y, w, h, label, style) | ||
| 240 | elif x >= 0.25 and w == 1.25 and h == 2: | ||
| 217 | render_key_isoenter(textpad, x, y, w, h, label, style) | 241 | render_key_isoenter(textpad, x, y, w, h, label, style) |
| 218 | elif w == 1.5 and h == 2: | 242 | elif w == 1.5 and h == 2: |
| 219 | render_key_baenter(textpad, x, y, w, h, label, style) | 243 | render_key_baenter(textpad, x, y, w, h, label, style) |
| @@ -331,3 +355,32 @@ def render_key_baenter(textpad, x, y, w, h, label, style): | |||
| 331 | textpad[y + 3][x - 3:x + w] = crn_line | 355 | textpad[y + 3][x - 3:x + w] = crn_line |
| 332 | textpad[y + 4][x - 3:x + w] = lab_line | 356 | textpad[y + 4][x - 3:x + w] = lab_line |
| 333 | textpad[y + 5][x - 3:x + w] = bot_line | 357 | textpad[y + 5][x - 3:x + w] = bot_line |
| 358 | |||
| 359 | |||
| 360 | def render_encoder(textpad, x, y, w, h, label, style): | ||
| 361 | box_chars = ENC_DRAWING_CHARACTERS[style] | ||
| 362 | x = ceil(x * 4) | ||
| 363 | y = ceil(y * 3) | ||
| 364 | w = ceil(w * 4) | ||
| 365 | h = ceil(h * 3) | ||
| 366 | |||
| 367 | label_len = w - 2 | ||
| 368 | label_leftover = label_len - len(label) | ||
| 369 | |||
| 370 | if len(label) > label_len: | ||
| 371 | label = label[:label_len] | ||
| 372 | |||
| 373 | label_blank = ' ' * label_len | ||
| 374 | label_border = box_chars['h'] * label_len | ||
| 375 | label_middle = label + ' ' * label_leftover | ||
| 376 | |||
| 377 | top_line = array('u', box_chars['tl'] + label_border + box_chars['tr']) | ||
| 378 | lab_line = array('u', box_chars['vl'] + label_middle + box_chars['vr']) | ||
| 379 | mid_line = array('u', box_chars['v'] + label_blank + box_chars['v']) | ||
| 380 | bot_line = array('u', box_chars['bl'] + label_border + box_chars['br']) | ||
| 381 | |||
| 382 | textpad[y][x:x + w] = top_line | ||
| 383 | textpad[y + 1][x:x + w] = lab_line | ||
| 384 | for i in range(h - 3): | ||
| 385 | textpad[y + i + 2][x:x + w] = mid_line | ||
| 386 | textpad[y + h - 1][x:x + w] = bot_line | ||
