implement multiple-choice for holdtap repeat

This commit is contained in:
xs5871 2022-10-14 15:20:55 +00:00 committed by Kyle Brown
parent 72735e0b5d
commit 178afdfeb1
3 changed files with 44 additions and 12 deletions

View File

@ -31,7 +31,7 @@ keyboard.modules.append(modtap)
## Custom HoldTap Behavior
The full ModTap signature is as follows:
```python
KC.MT(KC.TAP, KC.HOLD, prefer_hold=True, tap_interrupted=False, tap_time=None, repeat=False)
KC.MT(KC.TAP, KC.HOLD, prefer_hold=True, tap_interrupted=False, tap_time=None, repeat=HoldTapRepeat.NONE)
```
* `prefer_hold`: decides which keycode the ModTap key resolves to when another
key is pressed before the timeout finishes. When `True` the hold keycode is
@ -42,9 +42,10 @@ KC.MT(KC.TAP, KC.HOLD, prefer_hold=True, tap_interrupted=False, tap_time=None, r
* `tap_time`: length of the tap timeout in milliseconds.
* `repeat`: decides how to interpret repeated presses if they happen within
`tap_time` after a release.
When `True` the repeated press sends the previous keycode, no matter
how long the key remains pressed the second time.
This allows to hold the tap keycode, or repeatedly tap the hold keycode.
When `False` repeated presses are independent.
* `TAP`: repeat tap action, if previous action was a tap.
* `HOLD`: repeat hold action, if previous action was a hold.
* `ALL`: repeat all of the above.
* `NONE`: no repeat action (default), everything works as expected.
The `HoldTapRepeat` enum must be imported from `kmk.modules.holdtap`.
Each of these parameters can be set for every ModTap key individually.

View File

@ -15,6 +15,13 @@ class ActivationType:
REPEAT = const(4)
class HoldTapRepeat:
NONE = const(0)
TAP = const(1)
HOLD = const(2)
ALL = const(3)
class HoldTapKeyState:
def __init__(self, timeout_key, *args, **kwargs):
self.timeout_key = timeout_key
@ -31,7 +38,7 @@ class HoldTapKeyMeta:
prefer_hold=True,
tap_interrupted=False,
tap_time=None,
repeat=False,
repeat=HoldTapRepeat.NONE,
):
self.tap = tap
self.hold = hold
@ -155,13 +162,17 @@ class HoldTap(Module):
state = self.key_states[key]
keyboard.cancel_timeout(state.timeout_key)
repeat = key.meta.repeat & HoldTapRepeat.TAP
if state.activated == ActivationType.HOLD_TIMEOUT:
# release hold
self.ht_deactivate_hold(key, keyboard, *args, **kwargs)
repeat = key.meta.repeat & HoldTapRepeat.HOLD
elif state.activated == ActivationType.INTERRUPTED:
# release tap
self.ht_deactivate_on_interrupt(key, keyboard, *args, **kwargs)
if key.meta.prefer_hold:
repeat = key.meta.repeat & HoldTapRepeat.HOLD
elif state.activated == ActivationType.PRESSED:
# press and release tap because key released within tap time
self.ht_activate_tap(key, keyboard, *args, **kwargs)
@ -174,15 +185,16 @@ class HoldTap(Module):
self.ht_deactivate_tap(key, keyboard, *args, **kwargs)
# don't delete the key state right now in this case
tap_time = 0
if key.meta.repeat:
if repeat:
if key.meta.tap_time is None:
tap_time = self.tap_time
else:
tap_time = key.meta.tap_time
state.timeout_key = keyboard.set_timeout(
tap_time, lambda: self.key_states.pop(key)
)
state.timeout_key = keyboard.set_timeout(
tap_time, lambda: self.key_states.pop(key)
)
else:
del self.key_states[key]
return keyboard

View File

@ -1,6 +1,7 @@
import unittest
from kmk.keys import KC
from kmk.modules.holdtap import HoldTapRepeat
from kmk.modules.layers import Layers
from kmk.modules.modtap import ModTap
from kmk.modules.oneshot import OneShot
@ -275,7 +276,13 @@ class TestHoldTap(unittest.TestCase):
def test_holdtap_repeat(self):
keyboard = KeyboardTest(
[ModTap()],
[[KC.MT(KC.A, KC.B, repeat=True, tap_time=50)]],
[
[
KC.MT(KC.A, KC.B, repeat=HoldTapRepeat.ALL, tap_time=50),
KC.MT(KC.A, KC.B, repeat=HoldTapRepeat.TAP, tap_time=50),
KC.MT(KC.A, KC.B, repeat=HoldTapRepeat.HOLD, tap_time=50),
]
],
debug_enabled=False,
)
@ -331,6 +338,18 @@ class TestHoldTap(unittest.TestCase):
[{KC.A}, {}, {KC.B}, {}, {KC.A}, {}],
)
keyboard.test(
'tap repeat / no hold repeat ',
[(1, True), t_after, (1, False), (1, True), (1, False)],
[{KC.B}, {}, {KC.A}, {}],
)
keyboard.test(
'hold repeat / no tap repeat ',
[(2, True), (2, False), (2, True), t_after, (2, False)],
[{KC.A}, {}, {KC.B}, {}],
)
def test_oneshot(self):
keyboard = KeyboardTest(
[Layers(), ModTap(), OneShot()],