implement hold-tap interrupt on other key tap (i.e. release)
This commit is contained in:
		@@ -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):
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										25
									
								
								kmk/types.py
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								kmk/types.py
									
									
									
									
									
								
							@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user