Implement handling keys with modifiers

This commit is contained in:
Florian Bruhin 2014-02-05 10:54:56 +01:00
parent 630ff1cbf6
commit 9c19fd21d6
2 changed files with 85 additions and 8 deletions

View File

@ -4,7 +4,8 @@
import logging
import re
from PyQt5.QtCore import QObject, pyqtSignal
from PyQt5.QtCore import QObject, pyqtSignal, Qt
from PyQt5.QtGui import QKeySequence
from qutebrowser.commands.utils import (CommandParser, ArgumentCountError,
NoSuchCommandError)
@ -22,6 +23,7 @@ class KeyParser(QObject):
keystring_updated = pyqtSignal(str)
# Keybindings
bindings = {}
modifier_bindings = {}
commandparser = None
MATCH_PARTIAL = 0
@ -39,16 +41,69 @@ class KeyParser(QObject):
gg = scrollstart
"""
for (key, cmd) in sect.items():
logging.debug('registered key: {} -> {}'.format(key, cmd))
self.bindings[key] = cmd
if key.startswith('@') and key.endswith('@'):
# normalize keystring
keystr = self._normalize_keystr(key.strip('@'))
logging.debug('registered mod key: {} -> {}'.format(keystr,
cmd))
self.modifier_bindings[keystr] = cmd
else:
logging.debug('registered key: {} -> {}'.format(key, cmd))
self.bindings[key] = cmd
def handle(self, e):
"""Wrap _handle to emit keystring_updated after _handle."""
self._handle(e)
self.keystring_updated.emit(self.keystring)
"""Handle a new keypress and call the respective handlers.
def _handle(self, e):
"""Handle a new keypress.
e -- the KeyPressEvent from Qt
"""
handled = self._handle_modifier_key(e)
if not handled:
self._handle_single_key(e)
self.keystring_updated.emit(self.keystring)
def _handle_modifier_key(self, e):
"""Handle a new keypress with modifiers.
Returns True if the keypress has been handled, and False if not.
e -- the KeyPressEvent from Qt
"""
MODMASK2STR = {
Qt.ControlModifier: 'Ctrl',
Qt.AltModifier: 'Alt',
Qt.MetaModifier: 'Meta',
Qt.ShiftModifier: 'Shift'
}
if e.key() in [Qt.Key_Control, Qt.Key_Alt, Qt.Key_Shift, Qt.Key_Meta]:
# Only modifier pressed
return False
mod = e.modifiers()
modstr = ''
if not mod & (Qt.ControlModifier | Qt.AltModifier | Qt.MetaModifier):
# won't be a shortcut with modifiers
return False
for (mask, s) in MODMASK2STR.items():
if mod & mask:
modstr += s + '+'
keystr = QKeySequence(e.key()).toString()
try:
cmdstr = self.modifier_bindings[modstr + keystr]
except KeyError:
logging.debug('No binding found for {}.'.format(modstr + keystr))
return True
# FIXME use a common function for this
try:
self.commandparser.run(cmdstr, ignore_exc=False)
except NoSuchCommandError:
pass
except ArgumentCountError:
logging.debug('Filling statusbar with partial command {}'.format(
cmdstr))
self.set_cmd_text.emit(':{} '.format(cmdstr))
return True
def _handle_single_key(self, e):
"""Handle a new keypress with a single key (no modifiers).
Separates the keypress into count/command, then checks if it matches
any possible command, and either runs the command, ignores it, or
@ -128,3 +183,21 @@ class KeyParser(QObject):
continue
# no definitive and no partial matches if we arrived here
return (self.MATCH_NONE, None)
def _normalize_keystr(self, keystr):
"""Normalizes a keystring like Ctrl-Q to a keystring like Ctrl+Q.
keystr -- The key combination as a string.
"""
replacements = [
('Control', 'Ctrl'),
('Windows', 'Meta'),
('Mod1', 'Alt'),
('Mod4', 'Meta'),
]
for (orig, repl) in replacements:
keystr = keystr.replace(orig, repl)
for mod in ['Ctrl', 'Meta', 'Alt', 'Shift']:
keystr = keystr.replace(mod + '-', mod + '+')
keystr = QKeySequence(keystr).toString()
return keystr

View File

@ -48,6 +48,10 @@ default_config = """
pP = paste sel
Pp = tabpaste
PP = tabpaste sel
@Ctrl-Q@ = quit
@Ctrl-Shift-T@ = undo
@Ctrl-W@ = tabclose
@Ctrl-T@ = tabopen about:blank
[colors]
completion.fg = #333333