refactor tapdance on top of holdtap
This commit is contained in:
parent
7af0e11f75
commit
51f07d8558
@ -1,10 +1,4 @@
|
|||||||
from kmk.types import (
|
from kmk.types import KeySeqSleepMeta, LayerKeyMeta, ModTapKeyMeta, UnicodeModeKeyMeta
|
||||||
KeySeqSleepMeta,
|
|
||||||
LayerKeyMeta,
|
|
||||||
ModTapKeyMeta,
|
|
||||||
TapDanceKeyMeta,
|
|
||||||
UnicodeModeKeyMeta,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def key_seq_sleep_validator(ms):
|
def key_seq_sleep_validator(ms):
|
||||||
@ -37,9 +31,5 @@ def mod_tap_validator(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def tap_dance_key_validator(*codes):
|
|
||||||
return TapDanceKeyMeta(codes)
|
|
||||||
|
|
||||||
|
|
||||||
def unicode_mode_key_validator(mode):
|
def unicode_mode_key_validator(mode):
|
||||||
return UnicodeModeKeyMeta(mode)
|
return UnicodeModeKeyMeta(mode)
|
||||||
|
@ -1,124 +1,103 @@
|
|||||||
from kmk.key_validators import tap_dance_key_validator
|
from kmk.keys import KC, make_argumented_key
|
||||||
from kmk.keys import make_argumented_key
|
from kmk.modules.holdtap import ActivationType, HoldTap
|
||||||
from kmk.modules import Module
|
from kmk.types import HoldTapKeyMeta
|
||||||
from kmk.types import TapDanceKeyMeta
|
|
||||||
|
|
||||||
|
|
||||||
class TapDance(Module):
|
class TapDanceKeyMeta:
|
||||||
# User-configurable
|
def __init__(self, *keys, tap_time=None):
|
||||||
tap_time = 300
|
'''
|
||||||
|
Any key in the tapdance sequence that is not already a holdtap
|
||||||
|
key gets converted to a holdtap key with identical tap and hold
|
||||||
|
meta attributes.
|
||||||
|
'''
|
||||||
|
self.tap_time = tap_time
|
||||||
|
self.keys = []
|
||||||
|
|
||||||
# Internal State
|
for key in keys:
|
||||||
_tapping = False
|
if not isinstance(key.meta, HoldTapKeyMeta):
|
||||||
_tap_dance_counts = {}
|
ht_key = KC.HT(
|
||||||
_tap_timeout = None
|
tap=key,
|
||||||
_tap_side_effects = {}
|
hold=key,
|
||||||
|
prefer_hold=False,
|
||||||
|
tap_interrupted=False,
|
||||||
|
tap_time=self.tap_time,
|
||||||
|
)
|
||||||
|
self.keys.append(ht_key)
|
||||||
|
else:
|
||||||
|
self.keys.append(key)
|
||||||
|
self.keys = tuple(self.keys)
|
||||||
|
|
||||||
|
|
||||||
|
class TapDance(HoldTap):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
make_argumented_key(
|
make_argumented_key(
|
||||||
validator=tap_dance_key_validator,
|
validator=TapDanceKeyMeta,
|
||||||
names=('TAP_DANCE', 'TD'),
|
names=('TD',),
|
||||||
on_press=self.td_pressed,
|
on_press=self.td_pressed,
|
||||||
on_release=self.td_released,
|
on_release=self.td_released,
|
||||||
)
|
)
|
||||||
|
|
||||||
def during_bootup(self, keyboard):
|
self.td_counts = {}
|
||||||
return
|
|
||||||
|
|
||||||
def before_matrix_scan(self, keyboard):
|
|
||||||
return
|
|
||||||
|
|
||||||
def after_matrix_scan(self, keyboard):
|
|
||||||
return
|
|
||||||
|
|
||||||
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 process_key(self, keyboard, key, is_pressed, int_coord):
|
def process_key(self, keyboard, key, is_pressed, int_coord):
|
||||||
if self._tapping and is_pressed and not isinstance(key.meta, TapDanceKeyMeta):
|
if isinstance(key.meta, TapDanceKeyMeta):
|
||||||
for k, v in self._tap_dance_counts.items():
|
if key in self.td_counts:
|
||||||
if v:
|
return key
|
||||||
self._end_tap_dance(k, keyboard, hold=True)
|
return super().process_key(keyboard, key, is_pressed, int_coord)
|
||||||
keyboard.hid_pending = True
|
|
||||||
keyboard._send_hid()
|
|
||||||
keyboard.set_timeout(
|
|
||||||
False, lambda: keyboard.process_key(key, is_pressed)
|
|
||||||
)
|
|
||||||
return None
|
|
||||||
|
|
||||||
return key
|
|
||||||
|
|
||||||
def td_pressed(self, key, keyboard, *args, **kwargs):
|
def td_pressed(self, key, keyboard, *args, **kwargs):
|
||||||
if key not in self._tap_dance_counts or not self._tap_dance_counts[key]:
|
# active tap dance
|
||||||
self._tap_dance_counts[key] = 1
|
if key in self.td_counts:
|
||||||
self._tapping = True
|
count = self.td_counts[key]
|
||||||
|
kc = key.meta.keys[count]
|
||||||
|
keyboard.cancel_timeout(self.key_states[kc].timeout_key)
|
||||||
|
|
||||||
|
count += 1
|
||||||
|
|
||||||
|
# Tap dance reached the end of the list: send last tap in sequence
|
||||||
|
# and start from the beginning.
|
||||||
|
if count >= len(key.meta.keys):
|
||||||
|
self.key_states[kc].activated = ActivationType.RELEASED
|
||||||
|
self.on_tap_time_expired(kc, keyboard)
|
||||||
|
count = 0
|
||||||
|
|
||||||
|
# new tap dance
|
||||||
else:
|
else:
|
||||||
keyboard.cancel_timeout(self._tap_timeout)
|
count = 0
|
||||||
self._tap_dance_counts[key] += 1
|
|
||||||
|
|
||||||
if key not in self._tap_side_effects:
|
current_key = key.meta.keys[count]
|
||||||
self._tap_side_effects[key] = None
|
|
||||||
|
|
||||||
self._tap_timeout = keyboard.set_timeout(
|
self.ht_pressed(current_key, keyboard, *args, **kwargs)
|
||||||
self.tap_time, lambda: self._end_tap_dance(key, keyboard, hold=True)
|
self.td_counts[key] = count
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
# Add the active tap dance to key_states; `on_tap_time_expired` needs
|
||||||
|
# the back-reference.
|
||||||
|
self.key_states[current_key].tap_dance = key
|
||||||
|
|
||||||
def td_released(self, key, keyboard, *args, **kwargs):
|
def td_released(self, key, keyboard, *args, **kwargs):
|
||||||
has_side_effects = self._tap_side_effects[key] is not None
|
kc = key.meta.keys[self.td_counts[key]]
|
||||||
hit_max_defined_taps = self._tap_dance_counts[key] == len(key.meta.codes)
|
state = self.key_states[kc]
|
||||||
|
if state.activated == ActivationType.HOLD_TIMEOUT:
|
||||||
keyboard.cancel_timeout(self._tap_timeout)
|
# release hold
|
||||||
if has_side_effects or hit_max_defined_taps:
|
self.ht_deactivate_hold(kc, keyboard, *args, **kwargs)
|
||||||
self._end_tap_dance(key, keyboard)
|
del self.key_states[kc]
|
||||||
|
del self.td_counts[key]
|
||||||
|
elif state.activated == ActivationType.INTERRUPTED:
|
||||||
|
# release tap
|
||||||
|
self.ht_deactivate_on_interrupt(kc, keyboard, *args, **kwargs)
|
||||||
|
del self.key_states[kc]
|
||||||
|
del self.td_counts[key]
|
||||||
else:
|
else:
|
||||||
self._tap_timeout = keyboard.set_timeout(
|
# keep counting
|
||||||
self.tap_time, lambda: self._end_tap_dance(key, keyboard)
|
state.activated = ActivationType.RELEASED
|
||||||
)
|
|
||||||
|
|
||||||
return self
|
def on_tap_time_expired(self, key, keyboard, *args, **kwargs):
|
||||||
|
# Note: the `key` argument is the current holdtap key in the sequence,
|
||||||
def _end_tap_dance(self, key, keyboard, hold=False):
|
# not the tapdance key.
|
||||||
v = self._tap_dance_counts[key] - 1
|
state = self.key_states[key]
|
||||||
|
if state.activated == ActivationType.RELEASED:
|
||||||
if v < 0:
|
self.ht_activate_tap(key, keyboard, *args, **kwargs)
|
||||||
return self
|
keyboard._send_hid()
|
||||||
|
del self.td_counts[state.tap_dance]
|
||||||
if key in keyboard.keys_pressed:
|
super().on_tap_time_expired(key, keyboard, *args, **kwargs)
|
||||||
key_to_press = key.meta.codes[v]
|
|
||||||
keyboard.add_key(key_to_press)
|
|
||||||
self._tap_side_effects[key] = key_to_press
|
|
||||||
elif self._tap_side_effects[key]:
|
|
||||||
keyboard.remove_key(self._tap_side_effects[key])
|
|
||||||
self._tap_side_effects[key] = None
|
|
||||||
self._cleanup_tap_dance(key)
|
|
||||||
elif hold is False:
|
|
||||||
if key.meta.codes[v] in keyboard.keys_pressed:
|
|
||||||
keyboard.remove_key(key.meta.codes[v])
|
|
||||||
else:
|
|
||||||
keyboard.tap_key(key.meta.codes[v])
|
|
||||||
self._cleanup_tap_dance(key)
|
|
||||||
else:
|
|
||||||
key_to_press = key.meta.codes[v]
|
|
||||||
keyboard.add_key(key_to_press)
|
|
||||||
self._tap_side_effects[key] = key_to_press
|
|
||||||
self._tapping = 0
|
|
||||||
|
|
||||||
keyboard.hid_pending = True
|
|
||||||
|
|
||||||
return self
|
|
||||||
|
|
||||||
def _cleanup_tap_dance(self, key):
|
|
||||||
self._tap_dance_counts[key] = 0
|
|
||||||
self._tapping = any(count > 0 for count in self._tap_dance_counts.values())
|
|
||||||
return self
|
|
||||||
|
@ -50,8 +50,3 @@ class KeySeqSleepMeta:
|
|||||||
class UnicodeModeKeyMeta:
|
class UnicodeModeKeyMeta:
|
||||||
def __init__(self, mode):
|
def __init__(self, mode):
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
|
|
||||||
|
|
||||||
class TapDanceKeyMeta:
|
|
||||||
def __init__(self, codes):
|
|
||||||
self.codes = codes
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user