Keymap introspection for combos. (#19670)
This commit is contained in:
		
							parent
							
								
									433dc60686
								
							
						
					
					
						commit
						5faa23d54c
					
				
					 226 changed files with 533 additions and 729 deletions
				
			
		|  | @ -4,15 +4,12 @@ The Combo feature is a chording type solution for adding custom actions. It lets | |||
| 
 | ||||
| To enable this feature, you need to add `COMBO_ENABLE = yes` to your `rules.mk`. | ||||
| 
 | ||||
| Additionally, in your `config.h`, you'll need to specify the number of combos that you'll be using, by adding `#define COMBO_COUNT 1` (replacing 1 with the number that you're using). It is also possible to not define this and instead set the variable `COMBO_LEN` yourself. There's a trick where we don't need to think about this variable at all. More on this later. | ||||
| 
 | ||||
| 
 | ||||
| Then, in your `keymap.c` file, you'll need to define a sequence of keys, terminated with `COMBO_END`, and a structure to list the combination of keys, and its resulting action. | ||||
| 
 | ||||
| ```c | ||||
| const uint16_t PROGMEM test_combo1[] = {KC_A, KC_B, COMBO_END}; | ||||
| const uint16_t PROGMEM test_combo2[] = {KC_C, KC_D, COMBO_END}; | ||||
| combo_t key_combos[COMBO_COUNT] = { | ||||
| combo_t key_combos[] = { | ||||
|     COMBO(test_combo1, KC_ESC), | ||||
|     COMBO(test_combo2, LCTL(KC_Z)), // keycodes with modifiers are possible too! | ||||
| }; | ||||
|  | @ -33,7 +30,7 @@ It is possible to overlap combos. Before, with the example below both combos wou | |||
| ```c | ||||
| const uint16_t PROGMEM test_combo1[] = {LSFT_T(KC_A), LT(1, KC_B), COMBO_END}; | ||||
| const uint16_t PROGMEM test_combo2[] = {LSFT_T(KC_A), LT(1, KC_B), KC_C, COMBO_END}; | ||||
| combo_t key_combos[COMBO_COUNT] = { | ||||
| combo_t key_combos[] = { | ||||
|     COMBO(test_combo1, KC_ESC) | ||||
|     COMBO(test_combo2, KC_TAB) | ||||
| }; | ||||
|  | @ -41,17 +38,15 @@ combo_t key_combos[COMBO_COUNT] = { | |||
| 
 | ||||
| ## Examples | ||||
| 
 | ||||
| A long list of combos can be defined in an `enum` list that ends with `COMBO_LENGTH` and you can leave `COMBO_COUNT` undefined: | ||||
| A long list of combos can be defined in an `enum` list: | ||||
| 
 | ||||
| ```c | ||||
| enum combos { | ||||
|   AB_ESC, | ||||
|   JK_TAB, | ||||
|   QW_SFT, | ||||
|   SD_LAYER, | ||||
|   COMBO_LENGTH | ||||
|   SD_LAYER | ||||
| }; | ||||
| uint16_t COMBO_LEN = COMBO_LENGTH; // remove the COMBO_COUNT define and use this instead! | ||||
| 
 | ||||
| const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END}; | ||||
| const uint16_t PROGMEM jk_combo[] = {KC_J, KC_K, COMBO_END}; | ||||
|  | @ -72,9 +67,7 @@ For a more complicated implementation, you can use the `process_combo_event` fun | |||
| enum combo_events { | ||||
|   EM_EMAIL, | ||||
|   BSPC_LSFT_CLEAR, | ||||
|   COMBO_LENGTH | ||||
| }; | ||||
| uint16_t COMBO_LEN = COMBO_LENGTH; // remove the COMBO_COUNT define and use this instead! | ||||
| 
 | ||||
| const uint16_t PROGMEM email_combo[] = {KC_E, KC_M, COMBO_END}; | ||||
| const uint16_t PROGMEM clear_line_combo[] = {KC_BSPC, KC_LSFT, COMBO_END}; | ||||
|  | @ -259,18 +252,6 @@ bool combo_should_trigger(uint16_t combo_index, combo_t *combo, uint16_t keycode | |||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### Variable Length Combos | ||||
| If you leave `COMBO_COUNT` undefined in `config.h`, it allows you to programmatically declare the size of the Combo data structure and avoid updating `COMBO_COUNT`. Instead a variable called `COMBO_LEN` has to be set. It can be set with something similar to the following in `keymap.c`: `uint16_t COMBO_LEN = ARRAY_SIZE(key_combos);` or by adding `COMBO_LENGTH` as the *last* entry in the combo enum and then `uint16_t COMBO_LEN = COMBO_LENGTH;` as such: | ||||
| ```c | ||||
| enum myCombos { | ||||
|     ..., | ||||
|     COMBO_LENGTH | ||||
| }; | ||||
| uint16_t COMBO_LEN = COMBO_LENGTH; | ||||
| ``` | ||||
| Regardless of the method used to declare `COMBO_LEN`, this also requires to convert the `combo_t key_combos[COMBO_COUNT] = {...};` line to `combo_t key_combos[] = {...};`. | ||||
| 
 | ||||
| 
 | ||||
| ### Combo timer | ||||
| 
 | ||||
| Normally, the timer is started on the first key press and then reset on every subsequent key press within the `COMBO_TERM`. | ||||
|  | @ -300,10 +281,8 @@ Here's an example where a combo resolves to two modifiers, and on key releases t | |||
| 
 | ||||
| ```c | ||||
| enum combos { | ||||
|   AB_MODS, | ||||
|   COMBO_LENGTH | ||||
|   AB_MODS | ||||
| }; | ||||
| uint16_t COMBO_LEN = COMBO_LENGTH; | ||||
| 
 | ||||
| const uint16_t PROGMEM ab_combo[] = {KC_A, KC_B, COMBO_END}; | ||||
| 
 | ||||
|  | @ -415,6 +394,4 @@ SUBS(TH_THE,   "the",    KC_T, KC_H) // SUBS uses SEND_STRING to output the give | |||
| ... | ||||
| ``` | ||||
| 
 | ||||
| Now, you can update only one place to add or alter combos. You don't even need to remember to update the `COMBO_COUNT` or the `COMBO_LEN` variables at all. Everything is taken care of. Magic! | ||||
| 
 | ||||
| For small to huge ready made dictionaries of combos, you can check out http://combos.gboards.ca/. | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nick Brassel
						Nick Brassel