[Core] Tri Layer Keys (#19795)
Co-authored-by: wilba <wilba@wilba.tech> Co-authored-by: Pablo Martínez <58857054+elpekenin@users.noreply.github.com> Co-authored-by: Joel Challis <git@zvecr.com> Co-authored-by: Nick Brassel <nick@tzarc.org>master
parent
4002843797
commit
fe02abc479
@ -0,0 +1,18 @@ |
||||
{ |
||||
"keycodes": { |
||||
"0x7C77": { |
||||
"group": "quantum", |
||||
"key": "QK_TRI_LAYER_LOWER", |
||||
"aliases": [ |
||||
"TL_LOWR" |
||||
] |
||||
}, |
||||
"0x7C78": { |
||||
"group": "quantum", |
||||
"key": "QK_TRI_LAYER_UPPER", |
||||
"aliases": [ |
||||
"TL_UPPR" |
||||
] |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,48 @@ |
||||
# Tri Layers :id=tri-layers |
||||
|
||||
This enables support for the OLKB style "Tri Layer" keycodes. These function similar to the `MO` (momentary) function key, but if both the "Lower" and "Upper" keys are pressed, it activates a third "Adjust" layer. To enable this functionality, add this line to your `rules.mk`: |
||||
|
||||
```make |
||||
TRI_LAYER_ENABLE = yes |
||||
``` |
||||
|
||||
Note that the "upper", "lower" and "adjust" names don't have a particular significance, they are just used to identify and clarify the behavior. Layers are processed from highest numeric value to lowest, however the values are not required to be consecutive. |
||||
|
||||
For a detailed explanation of how the layer stack works, check out [Keymap Overview](keymap.md#keymap-and-layers). |
||||
|
||||
## Keycodes :id=keycodes |
||||
|
||||
| Keycode | Alias | Description | |
||||
|----------------------|-----------|---------------------------------------------------------------------------------------------------------| |
||||
| `QK_TRI_LAYER_LOWER` | `TL_LOWR` | Momentarily enables the "lower" layer. Enables the "adjust" layer if the "upper" layer is also enabled" | |
||||
| `QK_TRI_LAYER_UPPER` | `TL_UPPR` | Momentarily enables the "upper" layer. Enables the "adjust" layer if the "lower" layer is also enabled" | |
||||
|
||||
## Configuration |
||||
|
||||
To change the default values for the layers, you can change these defines, in your `config.h` |
||||
|
||||
| Config name | Default | Description | |
||||
|--------------------------|---------|------------------------------------------| |
||||
| `TRI_LAYER_LOWER_LAYER` | `1` | Sets the default for the "lower" layer. | |
||||
| `TRI_LAYER_UPPER_LAYER` | `2` | Sets the default for the "upper" layer. | |
||||
| `TRI_LAYER_ADJUST_LAYER` | `3` | Sets the default for the "adjust" layer. | |
||||
|
||||
Eg, if you wanted to set the "Adjust" layer to be layer 5, you'd add this to your `config.h`: |
||||
|
||||
```c |
||||
#define TRI_LAYER_ADJUST_LAYER 5 |
||||
``` |
||||
|
||||
## Functions |
||||
|
||||
| Function name | Description | |
||||
|----------------------------------------------|-------------------------------------------------| |
||||
| `set_tri_layer_lower_layer(layer)` | Changes the "lower" layer*. | |
||||
| `set_tri_layer_upper_layer(layer)` | Changes the "upper" layer*. | |
||||
| `set_tri_layer_adjust_layer(layer)` | Changes the "adjust" layer*. | |
||||
| `set_tri_layer_layers(lower, upper, adjust)` | Stes the "lower", "upper" and "adjust" layers*. | |
||||
| `get_tri_layer_lower_layer()` | Gets the current "lower" layer. | |
||||
| `get_tri_layer_upper_layer()` | Gets the current "upper" layer. | |
||||
| `get_tri_layer_adjust_layer()` | Gets the current "adjust" layer. | |
||||
|
||||
!> Note: these settings are not persisent, and will be reset to the default on power loss or power cycling of the controller. |
@ -0,0 +1,30 @@ |
||||
// Copyright 2023 QMK
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "process_tri_layer.h" |
||||
#include "tri_layer.h" |
||||
#include "action_layer.h" |
||||
|
||||
bool process_tri_layer(uint16_t keycode, keyrecord_t *record) { |
||||
switch (keycode) { |
||||
case QK_TRI_LAYER_LOWER: |
||||
if (record->event.pressed) { |
||||
layer_on(get_tri_layer_lower_layer()); |
||||
update_tri_layer(get_tri_layer_lower_layer(), get_tri_layer_upper_layer(), get_tri_layer_adjust_layer()); |
||||
} else { |
||||
layer_off(get_tri_layer_lower_layer()); |
||||
update_tri_layer(get_tri_layer_lower_layer(), get_tri_layer_upper_layer(), get_tri_layer_adjust_layer()); |
||||
} |
||||
return false; |
||||
case QK_TRI_LAYER_UPPER: |
||||
if (record->event.pressed) { |
||||
layer_on(get_tri_layer_upper_layer()); |
||||
update_tri_layer(get_tri_layer_lower_layer(), get_tri_layer_upper_layer(), get_tri_layer_adjust_layer()); |
||||
} else { |
||||
layer_off(get_tri_layer_upper_layer()); |
||||
update_tri_layer(get_tri_layer_lower_layer(), get_tri_layer_upper_layer(), get_tri_layer_adjust_layer()); |
||||
} |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
@ -0,0 +1,16 @@ |
||||
// Copyright 2023 QMK
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once |
||||
|
||||
#include "action.h" |
||||
|
||||
/**
|
||||
* @brief Handles tri layer behavior |
||||
* |
||||
* @param keycode the keycode |
||||
* @param record the key record structure |
||||
* @return true continue handling keycodes |
||||
* @return false stop handling keycodes |
||||
*/ |
||||
bool process_tri_layer(uint16_t keycode, keyrecord_t *record); |
@ -0,0 +1,39 @@ |
||||
// Copyright 2023 QMK
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "tri_layer.h" |
||||
#include <stdint.h> |
||||
|
||||
static uint8_t tri_layer_lower_layer = TRI_LAYER_LOWER_LAYER; |
||||
static uint8_t tri_layer_upper_layer = TRI_LAYER_UPPER_LAYER; |
||||
static uint8_t tri_layer_adjust_layer = TRI_LAYER_ADJUST_LAYER; |
||||
|
||||
void set_tri_layer_lower_layer(uint8_t layer) { |
||||
tri_layer_lower_layer = layer; |
||||
} |
||||
|
||||
void set_tri_layer_upper_layer(uint8_t layer) { |
||||
tri_layer_upper_layer = layer; |
||||
} |
||||
|
||||
void set_tri_layer_adjust_layer(uint8_t layer) { |
||||
tri_layer_adjust_layer = layer; |
||||
} |
||||
|
||||
void set_tri_layer_layers(uint8_t lower, uint8_t raise, uint8_t adjust) { |
||||
tri_layer_lower_layer = lower; |
||||
tri_layer_upper_layer = raise; |
||||
tri_layer_adjust_layer = adjust; |
||||
} |
||||
|
||||
uint8_t get_tri_layer_lower_layer(void) { |
||||
return tri_layer_lower_layer; |
||||
} |
||||
|
||||
uint8_t get_tri_layer_upper_layer(void) { |
||||
return tri_layer_upper_layer; |
||||
} |
||||
|
||||
uint8_t get_tri_layer_adjust_layer(void) { |
||||
return tri_layer_adjust_layer; |
||||
} |
@ -0,0 +1,59 @@ |
||||
// Copyright 2023 QMK
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <stdint.h> |
||||
|
||||
#ifndef TRI_LAYER_LOWER_LAYER |
||||
# define TRI_LAYER_LOWER_LAYER 1 |
||||
#endif |
||||
#ifndef TRI_LAYER_UPPER_LAYER |
||||
# define TRI_LAYER_UPPER_LAYER 2 |
||||
#endif |
||||
#ifndef TRI_LAYER_ADJUST_LAYER |
||||
# define TRI_LAYER_ADJUST_LAYER 3 |
||||
#endif |
||||
|
||||
/**
|
||||
* @brief Set the tri layer lower layer index |
||||
* |
||||
* @param layer |
||||
*/ |
||||
void set_tri_layer_lower_layer(uint8_t layer); |
||||
/**
|
||||
* @brief Set the tri layer upper layer index |
||||
* |
||||
* @param layer |
||||
*/ |
||||
void set_tri_layer_upper_layer(uint8_t layer); |
||||
/**
|
||||
* @brief Set the tri layer adjust layer index |
||||
* |
||||
* @param layer |
||||
*/ |
||||
void set_tri_layer_adjust_layer(uint8_t layer); |
||||
/**
|
||||
* @brief Set the tri layer indices |
||||
* |
||||
* @param lower |
||||
* @param upper |
||||
* @param adjust |
||||
*/ |
||||
void set_tri_layer_layers(uint8_t lower, uint8_t upper, uint8_t adjust); |
||||
/**
|
||||
* @brief Get the tri layer lower layer index |
||||
* |
||||
* @return uint8_t |
||||
*/ |
||||
uint8_t get_tri_layer_lower_layer(void); |
||||
/**
|
||||
* @brief Get the tri layer upper layer index |
||||
* |
||||
* @return uint8_t |
||||
*/ |
||||
uint8_t get_tri_layer_upper_layer(void); |
||||
/**
|
||||
* @brief Get the tri layer adjust layer index |
||||
* |
||||
* @return uint8_t |
||||
*/ |
||||
uint8_t get_tri_layer_adjust_layer(void); |
@ -0,0 +1,6 @@ |
||||
// Copyright 2021 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once |
||||
|
||||
#include "test_common.h" |
@ -0,0 +1,8 @@ |
||||
# Copyright 2021 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
# --------------------------------------------------------------------------------
|
||||
# Keep this file, even if it is empty, as a marker that this folder contains tests
|
||||
# --------------------------------------------------------------------------------
|
||||
|
||||
TRI_LAYER_ENABLE = yes
|
@ -0,0 +1,103 @@ |
||||
// Copyright 2021 Christopher Courtney, aka Drashna Jael're (@drashna) <drashna@live.com>
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "test_common.hpp" |
||||
|
||||
using testing::_; |
||||
using testing::InSequence; |
||||
|
||||
class TriLayer : public TestFixture {}; |
||||
|
||||
TEST_F(TriLayer, TriLayerLowerTest) { |
||||
TestDriver driver; |
||||
KeymapKey lower_layer_key = KeymapKey{0, 0, 0, QK_TRI_LAYER_LOWER}; |
||||
|
||||
set_keymap({lower_layer_key, KeymapKey{1, 0, 0, KC_TRNS}}); |
||||
|
||||
/* Press Lower. */ |
||||
EXPECT_NO_REPORT(driver); |
||||
lower_layer_key.press(); |
||||
run_one_scan_loop(); |
||||
EXPECT_TRUE(layer_state_is(get_tri_layer_lower_layer())); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer())); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer())); |
||||
VERIFY_AND_CLEAR(driver); |
||||
|
||||
/* Release Lower. */ |
||||
EXPECT_NO_REPORT(driver); |
||||
lower_layer_key.release(); |
||||
run_one_scan_loop(); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer())); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer())); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer())); |
||||
VERIFY_AND_CLEAR(driver); |
||||
} |
||||
|
||||
TEST_F(TriLayer, TriLayerUpperTest) { |
||||
TestDriver driver; |
||||
KeymapKey upper_layer_key = KeymapKey{0, 0, 0, QK_TRI_LAYER_UPPER}; |
||||
|
||||
set_keymap({upper_layer_key, KeymapKey{2, 0, 0, KC_TRNS}}); |
||||
|
||||
/* Press Raise. */ |
||||
EXPECT_NO_REPORT(driver); |
||||
upper_layer_key.press(); |
||||
run_one_scan_loop(); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer())); |
||||
EXPECT_TRUE(layer_state_is(get_tri_layer_upper_layer())); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer())); |
||||
VERIFY_AND_CLEAR(driver); |
||||
|
||||
/* Release Raise. */ |
||||
EXPECT_NO_REPORT(driver); |
||||
upper_layer_key.release(); |
||||
run_one_scan_loop(); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer())); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer())); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer())); |
||||
VERIFY_AND_CLEAR(driver); |
||||
} |
||||
|
||||
TEST_F(TriLayer, TriLayerAdjustTest) { |
||||
TestDriver driver; |
||||
KeymapKey lower_layer_key = KeymapKey{0, 0, 0, QK_TRI_LAYER_LOWER}; |
||||
KeymapKey upper_layer_key = KeymapKey{0, 1, 0, QK_TRI_LAYER_UPPER}; |
||||
|
||||
set_keymap({ |
||||
upper_layer_key, |
||||
lower_layer_key, |
||||
KeymapKey{1, 0, 0, KC_TRNS}, |
||||
KeymapKey{1, 1, 0, KC_TRNS}, |
||||
KeymapKey{2, 0, 0, KC_TRNS}, |
||||
KeymapKey{2, 1, 0, KC_TRNS}, |
||||
KeymapKey{3, 0, 0, KC_TRNS}, |
||||
KeymapKey{3, 1, 0, KC_TRNS}, |
||||
}); |
||||
|
||||
/* Press Lower, then upper, and release upper and then lower. */ |
||||
EXPECT_NO_REPORT(driver); |
||||
lower_layer_key.press(); |
||||
run_one_scan_loop(); |
||||
EXPECT_TRUE(layer_state_is(get_tri_layer_lower_layer())); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer())); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer())); |
||||
|
||||
upper_layer_key.press(); |
||||
run_one_scan_loop(); |
||||
EXPECT_TRUE(layer_state_is(get_tri_layer_lower_layer())); |
||||
EXPECT_TRUE(layer_state_is(get_tri_layer_upper_layer())); |
||||
EXPECT_TRUE(layer_state_is(get_tri_layer_adjust_layer())); |
||||
|
||||
lower_layer_key.release(); |
||||
run_one_scan_loop(); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer())); |
||||
EXPECT_TRUE(layer_state_is(get_tri_layer_upper_layer())); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer())); |
||||
|
||||
upper_layer_key.release(); |
||||
run_one_scan_loop(); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_lower_layer())); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_upper_layer())); |
||||
EXPECT_FALSE(layer_state_is(get_tri_layer_adjust_layer())); |
||||
VERIFY_AND_CLEAR(driver); |
||||
} |
Loading…
Reference in new issue