modholdandtap implementation

This commit is contained in:
Syed Hussaini 2022-04-22 13:35:49 -05:00 committed by xs5871
parent bd87b278b8
commit bce7959f66
3 changed files with 198 additions and 0 deletions

24
docs/modholdandtap.md Normal file
View File

@ -0,0 +1,24 @@
# ModHoldAndTap
This module allows to immitate the behaviour of ATL+TAB or CMD+TAB, etc.
Basically, it will hold the mod and tap a key on a layer other than default layer. The mod will be released when any other key is pressed or the layer key is released.
The key will do nothing when it is placed on the default layer
## Enabling the module
```python
from kmk.module.modholdandtap import ModHoldAndTap
modholdandtap = ModHoldAndTap()
keyboard.modules.append(modholdandtap)
keyboard.keymap = [
[
KC.MHAT(kc=KC.TAB, mod=KC.LALT),
KC.MHAT(KC.TAB, KC.LSFT(KC.LALT)),
],
]
```
## Keycodes
|Key |Description |
|-------------------------|-----------------------------------------------|
|`KC.MHAT(KC.key, KC.mod)`|holds the mod and taps the key |

View File

@ -0,0 +1,67 @@
from kmk.keys import make_argumented_key
from kmk.modules import Module
class ModHoldAndTapValidator:
def __init__(self, kc, mod):
self.kc = kc
self.mod = mod
class ModHoldAndTap(Module):
def __init__(self):
self._timeout_key = False
self._active = False
self._prev_key = None
make_argumented_key(
names=('MHAT',),
validator=ModHoldAndTapValidator,
)
def during_bootup(self, keyboard):
return
def before_matrix_scan(self, keyboard):
return
def process_key(self, keyboard, key, is_pressed, int_coord):
if isinstance(key.meta, ModHoldAndTapValidator):
layer_id = keyboard.active_layers[0]
if layer_id > 0:
if self._active and self._prev_key is not None and is_pressed:
# release previous key
self.release(keyboard, self._prev_key)
self._prev_key = key
if is_pressed:
keyboard.process_key(key.meta.mod, is_pressed)
self._active = True
keyboard.process_key(key.meta.kc, is_pressed)
elif self._active:
# release previous key if any other key is pressed
if self._prev_key is not None:
self.release(keyboard, self._prev_key)
return key
def before_hid_send(self, keyboard):
return
def after_hid_send(self, keyboard):
return
def on_powersave_enable(self, keyboard):
return
def on_powersave_disable(self, keyboard):
return
def after_matrix_scan(self, keyboard):
return
def release(self, keyboard, key):
keyboard.process_key(key.meta.mod, False)
self._active = False
self._prev_key = None

107
tests/test_modholdandtap.py Normal file
View File

@ -0,0 +1,107 @@
import unittest
from kmk.keys import KC
from kmk.modules.layers import Layers
from kmk.modules.modholdandtap import ModHoldAndTap
from tests.keyboard_test import KeyboardTest
class TestModHoldLayerTap(unittest.TestCase):
def test_basic_kmk_keyboard(self):
keyboard = KeyboardTest(
[Layers(), ModHoldAndTap()],
[
[
KC.A,
KC.B,
KC.MO(1),
KC.LT(1, KC.C),
KC.MHAT(kc=KC.TAB, mod=KC.LCTL(KC.LSFT)),
KC.F,
],
[
KC.MHAT(kc=KC.TAB, mod=KC.LGUI),
KC.MHAT(kc=KC.TAB, mod=KC.LSFT(KC.LGUI)),
KC.TRNS,
KC.B,
KC.N5,
],
[KC.A, KC.B, KC.N3, KC.N4, KC.N5],
],
debug_enabled=False,
)
keyboard.test(
'basic test',
[
(4, True),
(4, False),
100,
(4, True),
200,
(4, False),
100,
(1, True),
(1, False),
],
[
{KC.B},
{},
],
)
keyboard.test(
'basic test with MO',
[
(1, True),
(1, False),
(2, True),
200,
(0, True),
50,
(0, False),
50,
(0, True),
50,
(0, False),
(1, True),
(1, False),
50,
(1, True),
(1, False),
(0, True),
50,
(0, False),
(3, True),
(3, False),
(2, False),
100,
(4, True),
(4, False),
(1, True),
(1, False),
],
[
{KC.B},
{},
{KC.LGUI, KC.TAB},
{KC.LGUI},
{KC.LGUI, KC.TAB},
{KC.LGUI},
{KC.LSFT, KC.LGUI, KC.TAB},
{KC.LSFT, KC.LGUI},
{KC.LSFT, KC.LGUI, KC.TAB},
{KC.LSFT, KC.LGUI},
{KC.LGUI, KC.TAB},
{KC.LGUI},
{KC.B},
{},
{KC.B},
{},
],
)
if __name__ == '__main__':
unittest.main()