Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Tri_Layer_State submodule to layers module #658

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/en/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
- [SerialACE](serialace.md): [DANGER - _see module README_] Arbitrary Code Execution over the data serial.
- [Split](split_keyboards.md): Keyboards split in two. Seems ergonomic!
- [TapDance](tapdance.md): Different key actions depending on how often it is pressed.

- [TriLayerState](tri_layer_state.md): allows you to access a third layer by activating two other layers.
### Peripherals

- [ADNS9800](adns9800.md): Controlling ADNS9800 optical sensor
Expand Down
4 changes: 4 additions & 0 deletions docs/en/layers.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ different games). In this case, best practice is to have these layers be the low
defined first in your keymap. These layers are mutually-exclusive, so treat changing default
layers with `KC.DF()` the same way that you would treat using `KC.TO()`

### Tri-Layer-state
Allows you to access a third layer by activating two other layers.
Please see [TriLayerState](tri_layer_state.md) documentation for further information.

## Example Code
For our example, let's take a simple 3x3 macropad with two layers as follows:

Expand Down
81 changes: 81 additions & 0 deletions docs/en/tri_layer_state.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@


# TRI_LAYER_STATE

```python
from kmk.modules.layers import Layers, Trilayer
keyboard.modules.append(Layers())
keyboard.modules.append(Trilayer())
```

#Keycodes

|Key |Description |
|-----------------|--------------------------------------------------------------------------|
|`KC.RAISE` |Switches to the default or defined Raise Layer. |
|`KC.LOWER` |Switches to the default or defined Lower Layer. |

# Behavior

Tri-state layers is a feature that allows you to access a third layer by activating two other layers.

# Changing Layers
By default, the default layer is 0, lower is 1, raise is 2, adjust is 3.
To change which layers are affected by the "lower raise adjust" add this just above your keymap.
```python
Trilayer.place = [
0, #default 'or the layer it goes to when you release all keys. for most it is 0'
1, #lower 'or the 'KC.LOWER' key'
2, #raise 'or the 'KC.LOWER' key'
3, #adjust 'or the layer that is activated when lower or raise are pressed simultaneously'
]
```

# Without Tri Layer State
For most usecases, the way you would create a "Tri_Layer_State would be to have KC.MO(x) KC.MO(y) and KC.MO(z) on opposing layer keys. This works out fine for most people. The issue you run into is if you press KC.MO(X), Then KC.MO(Y), to get to KC.MO(Z); then you release KC.MO(X), and continue holding KC.MO(Y), it stays in KC.MO(Z). To get back into KC.MO(Y) you need to release KC.MO(Y) and press it again.

# With Tri Layer State
With Tri_Layer_state you can set opposing LOWER and RAISE keys. With the example above where you are not using tri layer state, when you release KC.MO(X) it will switch back to the state of KC.MO(Y). Each key acting independently.
With the way this is implimented in KMK (unlike in QMK) you can still access layer 3 independently.

Here is a Real world usecase:
You type a symbol on the raise layer, you have set enter to be on the lower layer. You quickly want to let go of raise and go to lower to press enter. You can do this as quickly as your finger releases raise with tri layer state. Without it, you could potentially hit lower before you let go of raise, causing you to be stuck on the adjust layer instead of lower.


# Example code
```python
import board

from kb import KMKKeyboard

from kmk.keys import KC, make_key
from kmk.modules.layers import Layers, Trilayer

keyboard = KMKKeyboard()

keyboard.modules.append(Layers())
keyboard.modules.append(Trilayer())

keyboard.keymap = [
[ # QWERTY
KC.A, KC.B, KC.C,
KC.LOWER, KC.SPACE, KC.RAISE,

],
[ # LOWER
KC.N1, KC.N2, KC.N3,
KC.LOWER, KC.SPACE, KC.RAISE,

],
[ # RAISE
KC.EXLM, KC.AT, KC.HASH,
KC.LOWER, KC.SPACE, KC.RAISE,

],
[ # ADJUST
KC.F1, KC.F2, KC.F3,
KC.LOWER, KC.SPACE, KC.RAISE

]
]
```
57 changes: 55 additions & 2 deletions kmk/modules/layers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
'''One layer isn't enough. Adds keys to get to more of them'''
from kmk.keys import KC, make_argumented_key
from kmk.keys import KC, make_argumented_key, make_key
from kmk.modules.holdtap import HoldTap, HoldTapKeyMeta
from kmk.utils import Debug

Expand Down Expand Up @@ -143,3 +142,57 @@ def _print_debug(self, keyboard):
# debug(f'__getitem__ {key}')
if debug.enabled:
debug(f'active_layers={keyboard.active_layers}')


# make tri_layer_state module like the one in qmk
class Trilayer(HoldTap):
'''Gives access to the keys used to enable the layer system'''

place = [
0, # default
1, # lower
2, # raise
3, # adjust
]

def __init__(self):
# Layers
super().__init__()
make_key(
names=('RAISE',),
on_press=self._raise_pressed,
on_release=self._raise_released,
)
make_key(
names=('LOWER',),
on_press=self._lower_pressed,
on_release=self._lower_released,
)

def _raise_pressed(self, key, keyboard, *args, **kwargs):
# if kc.LOWER intersects with kc.RAISE then set the active layer to adjust otherwise set the active layer to raise
if keyboard.active_layers[0] == self.place[1]:
keyboard.active_layers[0] = self.place[3]
else:
keyboard.active_layers[0] = self.place[2]

def _raise_released(self, key, keyboard, *args, **kwargs):
# if the active layer is adjust then set the active layer to lower otherwise set the active layer to default
if keyboard.active_layers[0] == self.place[3]:
keyboard.active_layers[0] = self.place[1]
else:
keyboard.active_layers[0] = self.place[0]

def _lower_pressed(self, key, keyboard, *args, **kwargs):
# if kc.LOWER intersects with kc.RAISE then set the active layer to adjust otherwise set the active layer to lower
if keyboard.active_layers[0] == self.place[2]:
keyboard.active_layers[0] = self.place[3]
else:
keyboard.active_layers[0] = self.place[1]

def _lower_released(self, key, keyboard, *args, **kwargs):
# if the active layer is adjust then set the active layer to raise otherwise set the active layer to default
if keyboard.active_layers[0] == self.place[3]:
keyboard.active_layers[0] = self.place[2]
else:
keyboard.active_layers[0] = self.place[0]