Apply key_mappings to KeySequences correctly

Fixes #3678
This commit is contained in:
Florian Bruhin 2018-03-06 21:39:57 +01:00
parent db7ccb0434
commit 0d94c17edc
4 changed files with 37 additions and 3 deletions

View File

@ -155,9 +155,8 @@ class BaseKeyParser(QObject):
match, binding = self._match_key(sequence)
if match == QKeySequence.NoMatch:
mappings = config.val.bindings.key_mappings
mapped = mappings.get(sequence, None)
if mapped is not None:
mapped = sequence.with_mappings(config.val.bindings.key_mappings)
if sequence != mapped:
self._debug_log("Mapped {} -> {}".format(
sequence, mapped))
match, binding = self._match_key(mapped)

View File

@ -321,6 +321,10 @@ class KeyInfo:
"""Get a QKeyEvent from this KeyInfo."""
return QKeyEvent(typ, self.key, self.modifiers, self.text())
def to_int(self):
"""Get the key as an integer (with key/modifiers)."""
return int(self.key) | int(self.modifiers)
class KeySequence:
@ -495,6 +499,18 @@ class KeySequence:
return self.__class__(*keys)
def with_mappings(self, mappings):
"""Get a new KeySequence with the given mappings applied."""
keys = []
for key in self._iter_keys():
key_seq = KeySequence(key)
if key_seq in mappings:
new_seq = mappings[key_seq]
assert len(new_seq) == 1
key = new_seq[0].to_int()
keys.append(key)
return self.__class__(*keys)
@classmethod
def parse(cls, keystr):
"""Parse a keystring like <Ctrl-x> or xyz and return a KeySequence."""

View File

@ -212,6 +212,14 @@ class TestHandle:
handle_text(Qt.Key_B)
assert not keyparser.execute.called
def test_mapping_in_key_chain(self, config_stub, handle_text, keyparser):
"""A mapping should work even as part of a keychain."""
config_stub.val.bindings.commands = {'normal':
{'aa': 'message-info aa'}}
keyparser._read_config('normal')
handle_text(Qt.Key_A, Qt.Key_X)
keyparser.execute.assert_called_once_with('message-info aa', None)
def test_binding_with_shift(self, keyparser, fake_keyevent):
"""Simulate a binding which involves shift."""
for key, modifiers in [(Qt.Key_Y, Qt.NoModifier),

View File

@ -377,6 +377,12 @@ class TestKeySequence:
with pytest.raises(keyutils.KeyParseError):
seq.append_event(event)
def test_with_mappings(self):
seq = keyutils.KeySequence.parse('foobar')
mappings = {keyutils.KeySequence('b'): keyutils.KeySequence('t')}
seq2 = seq.with_mappings(mappings)
assert seq2 == keyutils.KeySequence.parse('footar')
@pytest.mark.parametrize('keystr, expected', [
('<Ctrl-Alt-y>',
keyutils.KeySequence(Qt.ControlModifier | Qt.AltModifier | Qt.Key_Y)),
@ -443,6 +449,11 @@ def test_key_info_to_event():
assert ev.text() == 'A'
def test_key_info_to_int():
info = keyutils.KeyInfo(Qt.Key_A, Qt.ShiftModifier)
assert info.to_int() == Qt.Key_A | Qt.ShiftModifier
@pytest.mark.parametrize('key, printable', [
(Qt.Key_Control, False),
(Qt.Key_Escape, False),