implement hold-tap interrupt on other key tap (i.e. release)

This commit is contained in:
xs5871 2022-01-29 21:11:57 +00:00 committed by Kyle Brown
parent 5cae17c9f3
commit a685618480
3 changed files with 63 additions and 22 deletions

View File

@ -11,7 +11,7 @@ def key_seq_sleep_validator(ms):
return KeySeqSleepMeta(ms)
def layer_key_validator(layer, kc=None):
def layer_key_validator(layer, kc=None, tap_time=None):
'''
Validates the syntax (but not semantics) of a layer key call. We won't
have access to the keymap here, so we can't verify much of anything useful
@ -19,14 +19,24 @@ def layer_key_validator(layer, kc=None):
existing is mostly that Python will catch extraneous args/kwargs and error
out.
'''
return LayerKeyMeta(layer=layer, kc=kc)
return LayerKeyMeta(
layer=layer, kc=kc, prefer_hold=False, tap_interrupted=False, tap_time=tap_time
)
def mod_tap_validator(kc, mods=None, prefer_hold=True, tap_time=None):
def mod_tap_validator(
kc, mods=None, prefer_hold=True, tap_interrupted=False, tap_time=None
):
'''
Validates that mod tap keys are correctly used
'''
return ModTapKeyMeta(kc=kc, mods=mods, prefer_hold=prefer_hold, tap_time=tap_time)
return ModTapKeyMeta(
kc=kc,
mods=mods,
prefer_hold=prefer_hold,
tap_interrupted=tap_interrupted,
tap_time=tap_time,
)
def tap_dance_key_validator(*codes):

View File

@ -21,6 +21,7 @@ class HoldTap(Module):
tap_time = 300
def __init__(self):
self.key_buffer = set()
self.key_states = {}
def during_bootup(self, keyboard):
@ -33,19 +34,36 @@ class HoldTap(Module):
return
def process_key(self, keyboard, key, is_pressed):
'''Before other key down decide to send tap kc down.'''
'''Handle holdtap being interrupted by another key press/release.'''
current_key = key
for key, state in self.key_states.items():
if key == current_key:
continue
if is_pressed:
if state.activated == ActivationType.NOT_ACTIVATED:
# press tap because interrupted by other key
self.key_states[key].activated = ActivationType.INTERRUPTED
self.ht_activate_on_interrupt(
key, keyboard, *state.args, **state.kwargs
)
if state.activated != ActivationType.NOT_ACTIVATED:
continue
# holdtap is interrupted by another key event.
if (is_pressed and not key.meta.tap_interrupted) or (
not is_pressed and key.meta.tap_interrupted and self.key_buffer
):
keyboard.cancel_timeout(state.timeout_key)
self.key_states[key].activated = ActivationType.INTERRUPTED
self.ht_activate_on_interrupt(
key, keyboard, *state.args, **state.kwargs
)
keyboard._send_hid()
self.send_key_buffer(keyboard)
# if interrupt on release: store interrupting keys until one of them
# is released.
if key.meta.tap_interrupted:
if is_pressed:
self.key_buffer.add(current_key)
current_key = None
return current_key
def before_hid_send(self, keyboard):
@ -91,6 +109,7 @@ class HoldTap(Module):
False,
lambda: self.ht_deactivate_tap(key, keyboard, *args, **kwargs),
)
self.send_key_buffer(keyboard)
del self.key_states[key]
return keyboard
@ -103,6 +122,13 @@ class HoldTap(Module):
# press hold because timer expired after tap time
self.key_states[key].activated = ActivationType.HOLD_TIMEOUT
self.ht_activate_hold(key, keyboard, *args, **kwargs)
self.send_key_buffer(keyboard)
def send_key_buffer(self, keyboard):
for key in self.key_buffer:
key.on_press(keyboard)
keyboard._send_hid()
self.key_buffer.clear()
def ht_activate_hold(self, key, keyboard, *args, **kwargs):
pass

View File

@ -11,21 +11,26 @@ class AttrDict(dict):
return self[key]
class LayerKeyMeta:
def __init__(self, layer, kc=None, tap_time=None):
self.layer = layer
class HoldTapKeyMeta:
def __init__(self, kc=None, prefer_hold=True, tap_interrupted=False, tap_time=None):
self.kc = kc
self.tap_time = tap_time
class ModTapKeyMeta:
def __init__(self, kc=None, mods=None, prefer_hold=True, tap_time=None):
self.prefer_hold = prefer_hold
self.kc = kc
self.mods = mods
self.tap_interrupted = tap_interrupted
self.tap_time = tap_time
class LayerKeyMeta(HoldTapKeyMeta):
def __init__(self, layer, **kwargs):
super().__init__(**kwargs)
self.layer = layer
class ModTapKeyMeta(HoldTapKeyMeta):
def __init__(self, kc=None, mods=None, **kwargs):
super().__init__(kc=kc, **kwargs)
self.mods = mods
class KeySequenceMeta:
def __init__(self, seq):
self.seq = seq