Add key coord combos
Added an option to use key coord in the matrix to make combos. this allows to press / release combos regardless of layer and Key.
This commit is contained in:
		
				
					committed by
					
						
						xs5871
					
				
			
			
				
	
			
			
			
						parent
						
							ea3f59340d
						
					
				
				
					commit
					891cd1a67f
				
			@@ -27,6 +27,7 @@ Optional arguments that customize individual combos:
 | 
				
			|||||||
* `per_key_timeout`: If True, reset timeout on every key press (default for
 | 
					* `per_key_timeout`: If True, reset timeout on every key press (default for
 | 
				
			||||||
  sequences).
 | 
					  sequences).
 | 
				
			||||||
* `timeout`: Set the time window within which the match has to happen in ms.
 | 
					* `timeout`: Set the time window within which the match has to happen in ms.
 | 
				
			||||||
 | 
					* `match_coord`: If True, matches key position in the matrix.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
## Example Code
 | 
					## Example Code
 | 
				
			||||||
```python
 | 
					```python
 | 
				
			||||||
@@ -43,6 +44,8 @@ make_key(
 | 
				
			|||||||
combos.combos = [
 | 
					combos.combos = [
 | 
				
			||||||
    Chord((KC.A, KC.B), KC.LSFT),
 | 
					    Chord((KC.A, KC.B), KC.LSFT),
 | 
				
			||||||
    Chord((KC.A, KC.B, KC.C), KC.LALT),
 | 
					    Chord((KC.A, KC.B, KC.C), KC.LALT),
 | 
				
			||||||
 | 
					    Chord((0, 1), KC.ESC, match_coord=True),
 | 
				
			||||||
 | 
					    Chord((8, 9, 10), KC.MO(4), match_coord=True),
 | 
				
			||||||
    Sequence((KC.LEADER, KC.A, KC.B), KC.C),
 | 
					    Sequence((KC.LEADER, KC.A, KC.B), KC.C),
 | 
				
			||||||
    Sequence((KC.E, KC.F), KC.MYKEY, timeout=500, per_key_timeout=False, fast_reset=False)
 | 
					    Sequence((KC.E, KC.F), KC.MYKEY, timeout=500, per_key_timeout=False, fast_reset=False)
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
try:
 | 
					try:
 | 
				
			||||||
    from typing import Optional, Tuple
 | 
					    from typing import Optional, Tuple, Union
 | 
				
			||||||
except ImportError:
 | 
					except ImportError:
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
from micropython import const
 | 
					from micropython import const
 | 
				
			||||||
@@ -24,14 +24,16 @@ class Combo:
 | 
				
			|||||||
    _remaining = []
 | 
					    _remaining = []
 | 
				
			||||||
    _timeout = None
 | 
					    _timeout = None
 | 
				
			||||||
    _state = _ComboState.IDLE
 | 
					    _state = _ComboState.IDLE
 | 
				
			||||||
 | 
					    _match_coord = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(
 | 
					    def __init__(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        match: Tuple[Key, ...],
 | 
					        match: Tuple[Union[Key, int], ...],
 | 
				
			||||||
        result: Key,
 | 
					        result: Key,
 | 
				
			||||||
        fast_reset=None,
 | 
					        fast_reset=None,
 | 
				
			||||||
        per_key_timeout=None,
 | 
					        per_key_timeout=None,
 | 
				
			||||||
        timeout=None,
 | 
					        timeout=None,
 | 
				
			||||||
 | 
					        match_coord=None,
 | 
				
			||||||
    ):
 | 
					    ):
 | 
				
			||||||
        '''
 | 
					        '''
 | 
				
			||||||
        match: tuple of keys (KC.A, KC.B)
 | 
					        match: tuple of keys (KC.A, KC.B)
 | 
				
			||||||
@@ -45,22 +47,36 @@ class Combo:
 | 
				
			|||||||
            self.per_key_timeout = per_key_timeout
 | 
					            self.per_key_timeout = per_key_timeout
 | 
				
			||||||
        if timeout is not None:
 | 
					        if timeout is not None:
 | 
				
			||||||
            self.timeout = timeout
 | 
					            self.timeout = timeout
 | 
				
			||||||
 | 
					        if match_coord is not None:
 | 
				
			||||||
 | 
					            self._match_coord = match_coord
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __repr__(self):
 | 
					    def __repr__(self):
 | 
				
			||||||
        return f'{self.__class__.__name__}({[k.code for k in self.match]})'
 | 
					        return f'{self.__class__.__name__}({[k.code for k in self.match]})'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def matches(self, key):
 | 
					    def matches(self, key: Key, int_coord: int):
 | 
				
			||||||
        raise NotImplementedError
 | 
					        raise NotImplementedError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def has_match(self, key: Key, int_coord: int):
 | 
				
			||||||
 | 
					        return self._match_coord and int_coord in self.match or key in self.match
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def insert(self, key: Key, int_coord: int):
 | 
				
			||||||
 | 
					        if self._match_coord:
 | 
				
			||||||
 | 
					            self._remaining.insert(0, int_coord)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            self._remaining.insert(0, key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def reset(self):
 | 
					    def reset(self):
 | 
				
			||||||
        self._remaining = list(self.match)
 | 
					        self._remaining = list(self.match)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Chord(Combo):
 | 
					class Chord(Combo):
 | 
				
			||||||
    def matches(self, key):
 | 
					    def matches(self, key: Key, int_coord: int):
 | 
				
			||||||
        if key in self._remaining:
 | 
					        if not self._match_coord and key in self._remaining:
 | 
				
			||||||
            self._remaining.remove(key)
 | 
					            self._remaining.remove(key)
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
 | 
					        elif self._match_coord and int_coord in self._remaining:
 | 
				
			||||||
 | 
					            self._remaining.remove(int_coord)
 | 
				
			||||||
 | 
					            return True
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return False
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -70,8 +86,12 @@ class Sequence(Combo):
 | 
				
			|||||||
    per_key_timeout = True
 | 
					    per_key_timeout = True
 | 
				
			||||||
    timeout = 1000
 | 
					    timeout = 1000
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def matches(self, key):
 | 
					    def matches(self, key: Key, int_coord: int):
 | 
				
			||||||
        if self._remaining and self._remaining[0] == key:
 | 
					        if (
 | 
				
			||||||
 | 
					            not self._match_coord and self._remaining and self._remaining[0] == key
 | 
				
			||||||
 | 
					        ) or (
 | 
				
			||||||
 | 
					            self._match_coord and self._remaining and self._remaining[0] == int_coord
 | 
				
			||||||
 | 
					        ):
 | 
				
			||||||
            self._remaining.pop(0)
 | 
					            self._remaining.pop(0)
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
@@ -127,7 +147,7 @@ class Combos(Module):
 | 
				
			|||||||
        for combo in self.combos:
 | 
					        for combo in self.combos:
 | 
				
			||||||
            if combo._state != _ComboState.MATCHING:
 | 
					            if combo._state != _ComboState.MATCHING:
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            if combo.matches(key):
 | 
					            if combo.matches(key, int_coord):
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            combo._state = _ComboState.IDLE
 | 
					            combo._state = _ComboState.IDLE
 | 
				
			||||||
            if combo._timeout:
 | 
					            if combo._timeout:
 | 
				
			||||||
@@ -182,7 +202,7 @@ class Combos(Module):
 | 
				
			|||||||
        for combo in self.combos:
 | 
					        for combo in self.combos:
 | 
				
			||||||
            if combo._state != _ComboState.ACTIVE:
 | 
					            if combo._state != _ComboState.ACTIVE:
 | 
				
			||||||
                continue
 | 
					                continue
 | 
				
			||||||
            if key in combo.match:
 | 
					            if combo.has_match(key, int_coord):
 | 
				
			||||||
                # Deactivate combo if it matches current key.
 | 
					                # Deactivate combo if it matches current key.
 | 
				
			||||||
                self.deactivate(keyboard, combo)
 | 
					                self.deactivate(keyboard, combo)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -190,7 +210,7 @@ class Combos(Module):
 | 
				
			|||||||
                    self.reset_combo(keyboard, combo)
 | 
					                    self.reset_combo(keyboard, combo)
 | 
				
			||||||
                    self._key_buffer = []
 | 
					                    self._key_buffer = []
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    combo._remaining.insert(0, key)
 | 
					                    combo.insert(key, int_coord)
 | 
				
			||||||
                    combo._state = _ComboState.MATCHING
 | 
					                    combo._state = _ComboState.MATCHING
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                key = combo.result
 | 
					                key = combo.result
 | 
				
			||||||
@@ -203,7 +223,7 @@ class Combos(Module):
 | 
				
			|||||||
            for combo in self.combos:
 | 
					            for combo in self.combos:
 | 
				
			||||||
                if combo._state != _ComboState.MATCHING:
 | 
					                if combo._state != _ComboState.MATCHING:
 | 
				
			||||||
                    continue
 | 
					                    continue
 | 
				
			||||||
                if key not in combo.match:
 | 
					                if not combo.has_match(key, int_coord):
 | 
				
			||||||
                    continue
 | 
					                    continue
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                # Combo matches, but first key released before timeout.
 | 
					                # Combo matches, but first key released before timeout.
 | 
				
			||||||
@@ -216,7 +236,7 @@ class Combos(Module):
 | 
				
			|||||||
                    if combo.fast_reset:
 | 
					                    if combo.fast_reset:
 | 
				
			||||||
                        self.reset_combo(keyboard, combo)
 | 
					                        self.reset_combo(keyboard, combo)
 | 
				
			||||||
                    else:
 | 
					                    else:
 | 
				
			||||||
                        combo._remaining.insert(0, key)
 | 
					                        combo.insert(key, int_coord)
 | 
				
			||||||
                        combo._state = _ComboState.MATCHING
 | 
					                        combo._state = _ComboState.MATCHING
 | 
				
			||||||
                    self.reset(keyboard)
 | 
					                    self.reset(keyboard)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -236,7 +256,7 @@ class Combos(Module):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                # Anything between first and last key released.
 | 
					                # Anything between first and last key released.
 | 
				
			||||||
                else:
 | 
					                else:
 | 
				
			||||||
                    combo._remaining.insert(0, key)
 | 
					                    combo.insert(key, int_coord)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            # Don't propagate key-release events for keys that have been
 | 
					            # Don't propagate key-release events for keys that have been
 | 
				
			||||||
            # buffered. Append release events only if corresponding press is in
 | 
					            # buffered. Append release events only if corresponding press is in
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user