Support for tap behavior autorepeat for modtap and layertap keys.

Faulty logic corrected

Possibly a bug could happen whenever entering ht_press with a non-empty key_state outside of repeat context (but can it really happen?)

Some formatting to make github tests happy

Documentation for repeat behavior.

Make the tests happy again!

same...
This commit is contained in:
Aldoo 2022-06-16 23:01:37 +02:00 committed by Kyle Brown
parent 91032e123f
commit 5efd2688d7
2 changed files with 28 additions and 10 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)
KC.MT(KC.TAP, KC.HOLD, prefer_hold=True, tap_interrupted=False, tap_time=None, repeat=True)
```
* `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
@ -40,5 +40,10 @@ KC.MT(KC.TAP, KC.HOLD, prefer_hold=True, tap_interrupted=False, tap_time=None)
key press/down, or after the first other key up/release. Set to `True` for
interrupt on release.
* `tap_time`: length of the tap timeout in milliseconds.
* `repeat`: decides how to interpret a second press after a tap within the
timeout. When `True` the second press sends the tap keycode, no matter
how long the key remains pressed the second time. This allows the operating
system to trigger the autorepeat feature. Set it to `False` for handling
the second press as if no tap happened just before.
Each of these parameters can be set for every ModTap key individually.

View File

@ -12,6 +12,7 @@ class ActivationType:
RELEASED = const(1)
HOLD_TIMEOUT = const(2)
INTERRUPTED = const(3)
REPEAT = const(4)
class HoldTapKeyState:
@ -30,12 +31,14 @@ class HoldTapKeyMeta:
prefer_hold=True,
tap_interrupted=False,
tap_time=None,
repeat=False,
):
self.tap = tap
self.hold = hold
self.prefer_hold = prefer_hold
self.tap_interrupted = tap_interrupted
self.tap_time = tap_time
self.repeat = repeat
class HoldTap(Module):
@ -120,16 +123,21 @@ class HoldTap(Module):
return
def ht_pressed(self, key, keyboard, *args, **kwargs):
'''Do nothing yet, action resolves when key is released, timer expires or other key is pressed.'''
if key.meta.tap_time is None:
tap_time = self.tap_time
'''Unless in repeat mode, do nothing yet, action resolves when key is released, timer expires or other key is pressed.'''
state = self.key_states.get(key)
if state is not None and state.activated == ActivationType.RELEASED:
state.activated = ActivationType.REPEAT
self.ht_activate_tap(key, keyboard, *args, **kwargs)
else:
tap_time = key.meta.tap_time
timeout_key = keyboard.set_timeout(
tap_time,
lambda: self.on_tap_time_expired(key, keyboard, *args, **kwargs),
)
self.key_states[key] = HoldTapKeyState(timeout_key, *args, **kwargs)
if key.meta.tap_time is None:
tap_time = self.tap_time
else:
tap_time = key.meta.tap_time
timeout_key = keyboard.set_timeout(
tap_time,
lambda: self.on_tap_time_expired(key, keyboard, *args, **kwargs),
)
self.key_states[key] = HoldTapKeyState(timeout_key, *args, **kwargs)
return keyboard
def ht_released(self, key, keyboard, *args, **kwargs):
@ -152,6 +160,11 @@ class HoldTap(Module):
self.ht_deactivate_tap(key, keyboard, *args, **kwargs)
state.activated = ActivationType.RELEASED
self.send_key_buffer(keyboard)
# don't delete the key state right now in this case
if key.meta.repeat:
return keyboard
elif state.activated == ActivationType.REPEAT:
self.ht_deactivate_tap(key, keyboard, *args, **kwargs)
del self.key_states[key]
return keyboard