Add combo hook to allow per layer combo reference layers. (#16699)

Co-authored-by: Drashna Jaelre <drashna@live.com>
Co-authored-by: Sergey Vlasov <sigprof@gmail.com>
This commit is contained in:
Eric.a Gebhart 2023-02-12 11:31:04 -05:00 committed by GitHub
parent bbf7a20b33
commit db1eeea478
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 104 additions and 8 deletions

View file

@ -328,6 +328,51 @@ If you, for example, use multiple base layers for different key layouts, one for
With `#define COMBO_ONLY_FROM_LAYER 0` in config.h, the combos' keys are always checked from layer `0`, even if other layers are active.
### Combo reference layers by layer.
If not using `COMBO_ONLY_FROM_LAYER` it is possible to specify a
combo reference layer for any layer using the `combo_ref_from_layer` hook.
The combo macros automatically create this function from the `COMBO_REF_LAYER()`
entries given.
This function returns the assigned reference layer for the current layer.
if there is no match, it returns the default reference layer if set,
or the current layer otherwise. A default layer can be set with
`DEFAULT_REF_LAYER(_MY_COMBO_REF_LAYER)`
If not set, the default reference layer selection from the automatically generated
`combo-ref-from-layer()` will be the current layer.
The following `combo_ref_from_layer` function
will give a reference layer of _QWERTY for the _DVORAK layer and
will give the _NAV layer as a reference to it's self. All other layers
will have the default for their combo reference layer. If the default
is not set, all other layers will reference themselves.
```c
#define COMBO_REF_DEFAULT _MY_COMBO_LAYER
...
uint8_t combo_ref_from_layer(uint8_t layer){
switch (get_highest_layer(layer_state)){
case _DVORAK: return _QWERTY;
case _NAV: return _NAV;
default: return _MY_COMBO_LAYER;
}
return layer; // important if default is not in case.
}
```
The equivalent definition using the combo macros is this:
```c
COMBO_REF_LAYER(_DVORAK, _QWERTY)
COMBO_REF_LAYER(_NAV, _NAV)
DEFAULT_REF_LAYER(_MY_COMBO_LAYER).
```
## User callbacks
In addition to the keycodes, there are a few functions that you can use to set the status, or check it:
@ -350,6 +395,11 @@ First, you need to add `VPATH += keyboards/gboards` to your `rules.mk`. Next, in
Then, write your combos in `combos.def` file in the following manner:
```c
// Alternate reference layers by layer
// Layer Reference layer
COMBO_REF_LAYER(_DVORAK, _QWERTY) // reference the qwerty layer for dvorak.
COMBO_REF_LAYER(_NAV, _NAV) // explicit reference to self instead of the default.
// name result chord keys
COMB(AB_ESC, KC_ESC, KC_A, KC_B)
COMB(JK_TAB, KC_TAB, KC_J, KC_K)

View file

@ -1,4 +1,10 @@
// Keymap helpers
// define reference layers per layer.
#define REF_LAYER_FOR_LAYER(LAYER, REF_LAYER) \
case LAYER: return REF_LAYER;
#define DEF_REF_LAYER(LAYER) \
default: return LAYER;
#define K_ENUM(name, key, ...) name,
#define K_DATA(name, key, ...) const uint16_t PROGMEM cmb_##name[] = {__VA_ARGS__, COMBO_END};
@ -7,17 +13,22 @@
#define A_ENUM(name, string, ...) name,
#define A_DATA(name, string, ...) const uint16_t PROGMEM cmb_##name[] = {__VA_ARGS__, COMBO_END};
#define A_COMB(name, string, ...) [name] = COMBO_ACTION(cmb_##name),
#define A_ACTI(name, string, ...) \
case name: \
if (pressed) SEND_STRING(string); \
#define A_ACTI(name, string, ...) \
case name: \
if (pressed) SEND_STRING(string); \
break;
#define A_TOGG(name, layer, ...) \
case name: \
if (pressed) layer_invert(layer); \
#define A_TOGG(name, layer, ...) \
case name: \
if (pressed) layer_invert(layer); \
break;
#define BLANK(...)
#undef COMBO_REF_LAYER
#undef DEFAULT_REF_LAYER
#define COMBO_REF_LAYER BLANK
#define DEFAULT_REF_LAYER BLANK
// Generate data needed for combos/actions
// Create Enum
#undef COMB
@ -73,3 +84,26 @@ void process_combo_event(uint16_t combo_index, bool pressed) {
#undef COMB
#undef SUBS
#undef TOGG
// Allow reference layers per layer.
#define COMB BLANK
#define SUBS BLANK
#define TOGG BLANK
#undef DEFAULT_REF_LAYER
#undef COMBO_REF_LAYER
#define COMBO_REF_LAYER REF_LAYER_FOR_LAYER
#define DEFAULT_REF_LAYER DEF_REF_LAYER
uint8_t combo_ref_from_layer(uint8_t current_layer){
switch (current_layer){
#include "combos.def"
}
return current_layer;
}
#undef COMB
#undef SUBS
#undef TOGG
#undef COMBO_REF_LAYER
#undef DEFAULT_REF_LAYER

View file

@ -29,6 +29,12 @@ extern uint16_t COMBO_LEN;
__attribute__((weak)) void process_combo_event(uint16_t combo_index, bool pressed) {}
#ifndef COMBO_ONLY_FROM_LAYER
__attribute__((weak)) uint8_t combo_ref_from_layer(uint8_t layer) {
return layer;
}
#endif
#ifdef COMBO_MUST_HOLD_PER_COMBO
__attribute__((weak)) bool get_combo_must_hold(uint16_t index, combo_t *combo) {
return false;
@ -304,7 +310,7 @@ void apply_combo(uint16_t combo_index, combo_t *combo) {
#if defined(EXTRA_EXTRA_LONG_COMBOS)
uint32_t state = 0;
#elif defined(EXTRA_LONG_COMBOS)
uint16_t state = 0;
uint16_t state = 0;
#else
uint8_t state = 0;
#endif
@ -549,6 +555,12 @@ bool process_combo(uint16_t keycode, keyrecord_t *record) {
#ifdef COMBO_ONLY_FROM_LAYER
/* Only check keycodes from one layer. */
keycode = keymap_key_to_keycode(COMBO_ONLY_FROM_LAYER, record->event.key);
#else
uint8_t highest_layer = get_highest_layer(layer_state);
uint8_t ref_layer = combo_ref_from_layer(highest_layer);
if (ref_layer != highest_layer) {
keycode = keymap_key_to_keycode(ref_layer, record->event.key);
}
#endif
for (uint16_t idx = 0; idx < COMBO_LEN; ++idx) {