fix inconsistencies with chained holdtap keys
This commit is contained in:
parent
50b8554757
commit
614961521d
@ -45,6 +45,9 @@ class HoldTap(Module):
|
|||||||
def process_key(self, keyboard, key, is_pressed, int_coord):
|
def process_key(self, keyboard, key, is_pressed, int_coord):
|
||||||
'''Handle holdtap being interrupted by another key press/release.'''
|
'''Handle holdtap being interrupted by another key press/release.'''
|
||||||
current_key = key
|
current_key = key
|
||||||
|
send_buffer = False
|
||||||
|
append_buffer = False
|
||||||
|
|
||||||
for key, state in self.key_states.items():
|
for key, state in self.key_states.items():
|
||||||
if key == current_key:
|
if key == current_key:
|
||||||
continue
|
continue
|
||||||
@ -61,20 +64,28 @@ class HoldTap(Module):
|
|||||||
self.ht_activate_on_interrupt(
|
self.ht_activate_on_interrupt(
|
||||||
key, keyboard, *state.args, **state.kwargs
|
key, keyboard, *state.args, **state.kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
keyboard._send_hid()
|
keyboard._send_hid()
|
||||||
|
send_buffer = True
|
||||||
self.send_key_buffer(keyboard)
|
|
||||||
|
|
||||||
if state.activated == ActivationType.INTERRUPTED:
|
if state.activated == ActivationType.INTERRUPTED:
|
||||||
current_key = keyboard._find_key_in_map(int_coord)
|
current_key = keyboard._find_key_in_map(int_coord)
|
||||||
|
|
||||||
# if interrupt on release: store interrupting keys until one of them
|
# if interrupt on release: store interrupting keys until one of them
|
||||||
# is released.
|
# is released.
|
||||||
if key.meta.tap_interrupted:
|
if (
|
||||||
if is_pressed:
|
key.meta.tap_interrupted
|
||||||
|
and is_pressed
|
||||||
|
and not isinstance(current_key.meta, HoldTapKeyMeta)
|
||||||
|
):
|
||||||
|
append_buffer = True
|
||||||
|
|
||||||
|
# apply changes with 'side-effects' on key_states or the loop behaviour
|
||||||
|
# outside the loop.
|
||||||
|
if append_buffer:
|
||||||
self.key_buffer.append((int_coord, current_key))
|
self.key_buffer.append((int_coord, current_key))
|
||||||
current_key = None
|
current_key = None
|
||||||
|
elif send_buffer:
|
||||||
|
self.send_key_buffer(keyboard)
|
||||||
|
|
||||||
return current_key
|
return current_key
|
||||||
|
|
||||||
@ -105,9 +116,12 @@ class HoldTap(Module):
|
|||||||
|
|
||||||
def ht_released(self, key, keyboard, *args, **kwargs):
|
def ht_released(self, key, keyboard, *args, **kwargs):
|
||||||
'''On keyup, release mod or tap key.'''
|
'''On keyup, release mod or tap key.'''
|
||||||
if key in self.key_states:
|
if key not in self.key_states:
|
||||||
|
return keyboard
|
||||||
|
|
||||||
state = self.key_states[key]
|
state = self.key_states[key]
|
||||||
keyboard.cancel_timeout(state.timeout_key)
|
keyboard.cancel_timeout(state.timeout_key)
|
||||||
|
|
||||||
if state.activated == ActivationType.HOLD_TIMEOUT:
|
if state.activated == ActivationType.HOLD_TIMEOUT:
|
||||||
# release hold
|
# release hold
|
||||||
self.ht_deactivate_hold(key, keyboard, *args, **kwargs)
|
self.ht_deactivate_hold(key, keyboard, *args, **kwargs)
|
||||||
@ -121,8 +135,10 @@ class HoldTap(Module):
|
|||||||
False,
|
False,
|
||||||
lambda: self.ht_deactivate_tap(key, keyboard, *args, **kwargs),
|
lambda: self.ht_deactivate_tap(key, keyboard, *args, **kwargs),
|
||||||
)
|
)
|
||||||
|
state.activated = ActivationType.RELEASED
|
||||||
self.send_key_buffer(keyboard)
|
self.send_key_buffer(keyboard)
|
||||||
del self.key_states[key]
|
del self.key_states[key]
|
||||||
|
|
||||||
return keyboard
|
return keyboard
|
||||||
|
|
||||||
def on_tap_time_expired(self, key, keyboard, *args, **kwargs):
|
def on_tap_time_expired(self, key, keyboard, *args, **kwargs):
|
||||||
@ -145,8 +161,11 @@ class HoldTap(Module):
|
|||||||
del self.key_states[key]
|
del self.key_states[key]
|
||||||
|
|
||||||
def send_key_buffer(self, keyboard):
|
def send_key_buffer(self, keyboard):
|
||||||
for (int_coord, key) in self.key_buffer:
|
key_buffer = self.key_buffer
|
||||||
key.on_press(keyboard)
|
self.key_buffer = []
|
||||||
|
for (int_coord, key) in key_buffer:
|
||||||
|
new_key = keyboard._find_key_in_map(int_coord)
|
||||||
|
keyboard.pre_process_key(new_key, True, int_coord)
|
||||||
keyboard._send_hid()
|
keyboard._send_hid()
|
||||||
self.key_buffer.clear()
|
self.key_buffer.clear()
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ from tests.keyboard_test import KeyboardTest
|
|||||||
|
|
||||||
|
|
||||||
class TestHoldTap(unittest.TestCase):
|
class TestHoldTap(unittest.TestCase):
|
||||||
def test_basic_kmk_keyboard(self):
|
def test_holdtap(self):
|
||||||
keyboard = KeyboardTest(
|
keyboard = KeyboardTest(
|
||||||
[Layers(), ModTap(), OneShot()],
|
[Layers(), ModTap(), OneShot()],
|
||||||
[
|
[
|
||||||
@ -100,6 +100,168 @@ class TestHoldTap(unittest.TestCase):
|
|||||||
[{KC.N4}, {}],
|
[{KC.N4}, {}],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
keyboard.test(
|
||||||
|
'LT after tap time nested -> hold behavior',
|
||||||
|
[
|
||||||
|
(0, True),
|
||||||
|
350,
|
||||||
|
(1, True),
|
||||||
|
350,
|
||||||
|
(3, True),
|
||||||
|
(3, False),
|
||||||
|
(1, False),
|
||||||
|
(0, False),
|
||||||
|
],
|
||||||
|
[{KC.LCTL}, {KC.LCTL, KC.N4}, {KC.LCTL}, {}],
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_holdtap_chain(self):
|
||||||
|
keyboard = KeyboardTest(
|
||||||
|
[ModTap()],
|
||||||
|
[
|
||||||
|
[
|
||||||
|
KC.N0,
|
||||||
|
KC.MT(KC.N1, KC.LCTL, tap_time=50),
|
||||||
|
KC.MT(KC.N2, KC.LSFT, tap_interrupted=True, tap_time=50),
|
||||||
|
KC.MT(
|
||||||
|
KC.N3,
|
||||||
|
KC.LALT,
|
||||||
|
prefer_hold=False,
|
||||||
|
tap_interrupted=True,
|
||||||
|
tap_time=50,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
debug_enabled=False,
|
||||||
|
)
|
||||||
|
# t_within = 40
|
||||||
|
t_after = 60
|
||||||
|
|
||||||
|
keyboard.test(
|
||||||
|
'chained 0',
|
||||||
|
[(1, True), (2, True), (0, True), (0, False), (2, False), (1, False)],
|
||||||
|
[
|
||||||
|
{KC.LCTL},
|
||||||
|
{KC.LCTL, KC.LSFT},
|
||||||
|
{KC.LCTL, KC.LSFT, KC.N0},
|
||||||
|
{KC.LCTL, KC.LSFT},
|
||||||
|
{KC.LCTL},
|
||||||
|
{},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
keyboard.test(
|
||||||
|
'chained 1',
|
||||||
|
[(2, True), (1, True), (0, True), (0, False), (1, False), (2, False)],
|
||||||
|
[
|
||||||
|
{KC.LCTL},
|
||||||
|
{KC.LCTL, KC.LSFT},
|
||||||
|
{KC.LCTL, KC.LSFT, KC.N0},
|
||||||
|
{KC.LCTL, KC.LSFT},
|
||||||
|
{KC.LSFT},
|
||||||
|
{},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
keyboard.test(
|
||||||
|
'chained 2',
|
||||||
|
[(1, True), (2, True), (0, True), (1, False), (2, False), (0, False)],
|
||||||
|
[
|
||||||
|
{KC.LCTL},
|
||||||
|
{KC.LCTL, KC.LSFT},
|
||||||
|
{KC.LCTL, KC.LSFT, KC.N0},
|
||||||
|
{KC.LSFT, KC.N0},
|
||||||
|
{KC.N0},
|
||||||
|
{},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
keyboard.test(
|
||||||
|
'chained 3',
|
||||||
|
[(1, True), (3, True), (0, True), (0, False), (1, False), (3, False)],
|
||||||
|
[
|
||||||
|
{KC.LCTL},
|
||||||
|
{KC.LCTL, KC.N3},
|
||||||
|
{KC.LCTL, KC.N3, KC.N0},
|
||||||
|
{KC.LCTL, KC.N3},
|
||||||
|
{KC.N3},
|
||||||
|
{},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
keyboard.test(
|
||||||
|
'chained 4',
|
||||||
|
[(1, True), (3, True), (0, True), (3, False), (1, False), (0, False)],
|
||||||
|
[{KC.LCTL}, {KC.LCTL, KC.N3, KC.N0}, {KC.LCTL, KC.N0}, {KC.N0}, {}],
|
||||||
|
)
|
||||||
|
|
||||||
|
keyboard.test(
|
||||||
|
'chained 5',
|
||||||
|
[(3, True), (1, True), (0, True), (0, False), (1, False), (3, False)],
|
||||||
|
[
|
||||||
|
{KC.LCTL},
|
||||||
|
{KC.LCTL, KC.N3},
|
||||||
|
{KC.LCTL, KC.N3, KC.N0},
|
||||||
|
{KC.LCTL, KC.N3},
|
||||||
|
{KC.N3},
|
||||||
|
{},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
keyboard.test(
|
||||||
|
'chained 6',
|
||||||
|
[
|
||||||
|
(3, True),
|
||||||
|
(1, True),
|
||||||
|
t_after,
|
||||||
|
(0, True),
|
||||||
|
(0, False),
|
||||||
|
(1, False),
|
||||||
|
(3, False),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{KC.LCTL, KC.LALT},
|
||||||
|
{KC.LCTL, KC.LALT, KC.N0},
|
||||||
|
{KC.LCTL, KC.LALT},
|
||||||
|
{KC.LALT},
|
||||||
|
{},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
keyboard.test(
|
||||||
|
'chained 7',
|
||||||
|
[
|
||||||
|
(1, True),
|
||||||
|
(3, True),
|
||||||
|
t_after,
|
||||||
|
(0, True),
|
||||||
|
(0, False),
|
||||||
|
(1, False),
|
||||||
|
(3, False),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{KC.LCTL},
|
||||||
|
{KC.LCTL, KC.LALT},
|
||||||
|
{KC.LCTL, KC.LALT, KC.N0},
|
||||||
|
{KC.LCTL, KC.LALT},
|
||||||
|
{KC.LALT},
|
||||||
|
{},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
keyboard.test(
|
||||||
|
'chained 8',
|
||||||
|
[(2, True), (3, True), (0, True), (0, False), (2, False), (3, False)],
|
||||||
|
[
|
||||||
|
{KC.LSFT},
|
||||||
|
{KC.LSFT, KC.N3},
|
||||||
|
{KC.LSFT, KC.N3, KC.N0},
|
||||||
|
{KC.LSFT, KC.N3},
|
||||||
|
{KC.N3},
|
||||||
|
{},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
# TODO test TT
|
# TODO test TT
|
||||||
|
|
||||||
def test_oneshot(self):
|
def test_oneshot(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user