From 0b6d2c2b0a0b1101e4f84c024e4420c27fd2aee9 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 28 Feb 2018 10:21:02 +0100 Subject: [PATCH] Make all key names work --- qutebrowser/keyinput/keyutils.py | 127 ++++++++++++--------------- tests/unit/keyinput/key_data.py | 88 +++++++++---------- tests/unit/keyinput/test_keyutils.py | 37 +++----- 3 files changed, 110 insertions(+), 142 deletions(-) diff --git a/qutebrowser/keyinput/keyutils.py b/qutebrowser/keyinput/keyutils.py index c3c5baf40..5280c8a9b 100644 --- a/qutebrowser/keyinput/keyutils.py +++ b/qutebrowser/keyinput/keyutils.py @@ -39,69 +39,63 @@ def _key_to_string(key): Return: A name of the key as a string. """ - return QKeySequence(key).toString() # FIXME special_names_str = { # Some keys handled in a weird way by QKeySequence::toString. # See https://bugreports.qt.io/browse/QTBUG-40030 # Most are unlikely to be ever needed, but you never know ;) # For dead/combining keys, we return the corresponding non-combining # key, as that's easier to add to the config. - 'Key_Blue': 'Blue', - 'Key_Calendar': 'Calendar', - 'Key_ChannelDown': 'Channel Down', - 'Key_ChannelUp': 'Channel Up', - 'Key_ContrastAdjust': 'Contrast Adjust', - 'Key_Dead_Abovedot': '˙', - 'Key_Dead_Abovering': '˚', - 'Key_Dead_Acute': '´', - 'Key_Dead_Belowdot': 'Belowdot', - 'Key_Dead_Breve': '˘', - 'Key_Dead_Caron': 'ˇ', - 'Key_Dead_Cedilla': '¸', - 'Key_Dead_Circumflex': '^', - 'Key_Dead_Diaeresis': '¨', - 'Key_Dead_Doubleacute': '˝', - 'Key_Dead_Grave': '`', - 'Key_Dead_Hook': 'Hook', - 'Key_Dead_Horn': 'Horn', - 'Key_Dead_Iota': 'Iota', - 'Key_Dead_Macron': '¯', - 'Key_Dead_Ogonek': '˛', - 'Key_Dead_Semivoiced_Sound': 'Semivoiced Sound', - 'Key_Dead_Tilde': '~', - 'Key_Dead_Voiced_Sound': 'Voiced Sound', - 'Key_Exit': 'Exit', - 'Key_Green': 'Green', - 'Key_Guide': 'Guide', - 'Key_Info': 'Info', - 'Key_LaunchG': 'LaunchG', - 'Key_LaunchH': 'LaunchH', - 'Key_MediaLast': 'MediaLast', - 'Key_Memo': 'Memo', - 'Key_MicMute': 'Mic Mute', - 'Key_Mode_switch': 'Mode switch', - 'Key_Multi_key': 'Multi key', - 'Key_PowerDown': 'Power Down', - 'Key_Red': 'Red', - 'Key_Settings': 'Settings', - 'Key_SingleCandidate': 'Single Candidate', - 'Key_ToDoList': 'Todo List', - 'Key_TouchpadOff': 'Touchpad Off', - 'Key_TouchpadOn': 'Touchpad On', - 'Key_TouchpadToggle': 'Touchpad toggle', - 'Key_Yellow': 'Yellow', - 'Key_Alt': 'Alt', - 'Key_AltGr': 'AltGr', - 'Key_Control': 'Control', - 'Key_Direction_L': 'Direction L', - 'Key_Direction_R': 'Direction R', - 'Key_Hyper_L': 'Hyper L', - 'Key_Hyper_R': 'Hyper R', - 'Key_Meta': 'Meta', - 'Key_Shift': 'Shift', - 'Key_Super_L': 'Super L', - 'Key_Super_R': 'Super R', - 'Key_unknown': 'Unknown', + + 'Super_L': 'Super L', + 'Super_R': 'Super R', + 'Hyper_L': 'Hyper L', + 'Hyper_R': 'Hyper R', + 'Direction_L': 'Direction L', + 'Direction_R': 'Direction R', + + 'Shift': 'Shift', + 'Control': 'Control', + 'Meta': 'Meta', + 'Alt': 'Alt', + + 'AltGr': 'AltGr', + 'Multi_key': 'Multi key', + 'SingleCandidate': 'Single Candidate', + 'Mode_switch': 'Mode switch', + 'Dead_Grave': '`', + 'Dead_Acute': '´', + 'Dead_Circumflex': '^', + 'Dead_Tilde': '~', + 'Dead_Macron': '¯', + 'Dead_Breve': '˘', + 'Dead_Abovedot': '˙', + 'Dead_Diaeresis': '¨', + 'Dead_Abovering': '˚', + 'Dead_Doubleacute': '˝', + 'Dead_Caron': 'ˇ', + 'Dead_Cedilla': '¸', + 'Dead_Ogonek': '˛', + 'Dead_Iota': 'Iota', + 'Dead_Voiced_Sound': 'Voiced Sound', + 'Dead_Semivoiced_Sound': 'Semivoiced Sound', + 'Dead_Belowdot': 'Belowdot', + 'Dead_Hook': 'Hook', + 'Dead_Horn': 'Horn', + + 'Memo': 'Memo', + 'ToDoList': 'To Do List', + 'Calendar': 'Calendar', + 'ContrastAdjust': 'Contrast Adjust', + 'LaunchG': 'Launch (G)', + 'LaunchH': 'Launch (H)', + + 'MediaLast': 'Media Last', + + 'unknown': 'Unknown', + + # For some keys, we just want a different name + 'Backtab': 'Tab', + 'Escape': 'Escape', } # We now build our real special_names dict from the string mapping above. # The reason we don't do this directly is that certain Qt versions don't @@ -109,23 +103,14 @@ def _key_to_string(key): special_names = {} for k, v in special_names_str.items(): try: - special_names[getattr(Qt, k)] = v + special_names[getattr(Qt, 'Key_' + k)] = v except AttributeError: pass - # Now we check if the key is any special one - if not, we use - # QKeySequence::toString. - try: + + if key in special_names: return special_names[key] - except KeyError: - name = QKeySequence(key).toString() - morphings = { - 'Backtab': 'Tab', - 'Esc': 'Escape', - } - if name in morphings: - return morphings[name] - else: - return name + + return QKeySequence(key).toString() class KeyParseError(Exception): diff --git a/tests/unit/keyinput/key_data.py b/tests/unit/keyinput/key_data.py index 071fc8f43..2e9dda46a 100644 --- a/tests/unit/keyinput/key_data.py +++ b/tests/unit/keyinput/key_data.py @@ -34,9 +34,9 @@ class Key: # From enum Key in qt5/qtbase/src/corelib/global/qnamespace.h KEYS = [ ### misc keys - Key('Escape', 'Esc'), + Key('Escape'), # qutebrowser has a different name from Qt Key('Tab'), - Key('Backtab'), + Key('Backtab', 'Tab'), # qutebrowser has a different name from Qt Key('Backspace'), Key('Return'), Key('Enter'), @@ -56,10 +56,10 @@ KEYS = [ Key('PageUp', 'PgUp'), Key('PageDown', 'PgDown'), ### modifiers - Key('Shift', '\u17c0\udc20'), # FIXME - Key('Control', '\u17c0\udc21'), # FIXME - Key('Meta', '\u17c0\udc22'), # FIXME - Key('Alt', '\u17c0\udc23'), # FIXME + Key('Shift'), + Key('Control'), + Key('Meta'), + Key('Alt'), Key('CapsLock'), Key('NumLock'), Key('ScrollLock'), @@ -101,17 +101,17 @@ KEYS = [ Key('F34'), Key('F35'), ### extra keys - Key('Super_L', '\u17c0\udc53'), # FIXME - Key('Super_R', '\u17c0\udc54'), # FIXME + Key('Super_L', 'Super L'), + Key('Super_R', 'Super R'), Key('Menu'), - Key('Hyper_L', '\u17c0\udc56'), # FIXME - Key('Hyper_R', '\u17c0\udc57'), # FIXME + Key('Hyper_L', 'Hyper L'), + Key('Hyper_R', 'Hyper R'), Key('Help'), - Key('Direction_L', '\u17c0\udc59'), # FIXME - Key('Direction_R', '\u17c0\udc60'), # FIXME + Key('Direction_L', 'Direction L'), + Key('Direction_R', 'Direction R'), ### 7 bit printable ASCII Key('Space'), - Key('Any', 'Space'), # FIXME + Key('Any', 'Space'), # Same value Key('Exclam', '!'), Key('QuoteDbl', '"'), Key('NumberSign', '#'), @@ -253,15 +253,15 @@ KEYS = [ ### you are writing your own input method ### International & multi-key character composition - Key('AltGr', '\u17c4\udd03'), # FIXME - Key('Multi_key', '\u17c4\udd20'), # FIXME Multi-key character compose + Key('AltGr'), + Key('Multi_key', 'Multi key'), # Multi-key character compose Key('Codeinput', 'Code input'), - Key('SingleCandidate', '\u17c4\udd3c'), # FIXME + Key('SingleCandidate', 'Single Candidate'), Key('MultipleCandidate', 'Multiple Candidate'), Key('PreviousCandidate', 'Previous Candidate'), ### Misc Functions - Key('Mode_switch', '\u17c4\udd7e'), # FIXME Character set switch + Key('Mode_switch', 'Mode switch'), # Character set switch # Key('script_switch'), # Alias for mode_switch ### Japanese keyboard support @@ -309,25 +309,25 @@ KEYS = [ # Key('Hangul_switch', 'Hangul switch'), # Alias for mode_switch # dead keys (X keycode - 0xED00 to avoid the conflict), - Key('Dead_Grave', '\u17c4\ude50'), # FIXME - Key('Dead_Acute', '\u17c4\ude51'), # FIXME - Key('Dead_Circumflex', '\u17c4\ude52'), # FIXME - Key('Dead_Tilde', '\u17c4\ude53'), # FIXME - Key('Dead_Macron', '\u17c4\ude54'), # FIXME - Key('Dead_Breve', '\u17c4\ude55'), # FIXME - Key('Dead_Abovedot', '\u17c4\ude56'), # FIXME - Key('Dead_Diaeresis', '\u17c4\ude57'), # FIXME - Key('Dead_Abovering', '\u17c4\ude58'), # FIXME - Key('Dead_Doubleacute', '\u17c4\ude59'), # FIXME - Key('Dead_Caron', '\u17c4\ude5a'), # FIXME - Key('Dead_Cedilla', '\u17c4\ude5b'), # FIXME - Key('Dead_Ogonek', '\u17c4\ude5c'), # FIXME - Key('Dead_Iota', '\u17c4\ude5d'), # FIXME - Key('Dead_Voiced_Sound', '\u17c4\ude5e'), # FIXME - Key('Dead_Semivoiced_Sound', '\u17c4\ude5f'), # FIXME - Key('Dead_Belowdot', '\u17c4\ude60'), # FIXME - Key('Dead_Hook', '\u17c4\ude61'), # FIXME - Key('Dead_Horn', '\u17c4\ude62'), # FIXME + Key('Dead_Grave', '`'), + Key('Dead_Acute', '´'), + Key('Dead_Circumflex', '^'), + Key('Dead_Tilde', '~'), + Key('Dead_Macron', '¯'), + Key('Dead_Breve', '˘'), + Key('Dead_Abovedot', '˙'), + Key('Dead_Diaeresis', '¨'), + Key('Dead_Abovering', '˚'), + Key('Dead_Doubleacute', '˝'), + Key('Dead_Caron', 'ˇ'), + Key('Dead_Cedilla', '¸'), + Key('Dead_Ogonek', '˛'), + Key('Dead_Iota', 'Iota'), + Key('Dead_Voiced_Sound', 'Voiced Sound'), + Key('Dead_Semivoiced_Sound', 'Semivoiced Sound'), + Key('Dead_Belowdot', 'Belowdot'), + Key('Dead_Hook', 'Hook'), + Key('Dead_Horn', 'Horn'), # Not in Qt 5.10, so data may be wrong! Key('Dead_Stroke'), @@ -415,7 +415,7 @@ KEYS = [ Key('Eject'), Key('ScreenSaver', 'Screensaver'), Key('WWW'), - Key('Memo', '\u17c0\udcbc'), # FIXME + Key('Memo', 'Memo'), Key('LightBulb'), Key('Shop'), Key('History'), @@ -431,7 +431,7 @@ KEYS = [ Key('Book'), Key('CD'), Key('Calculator'), - Key('ToDoList', '\u17c0\udccc'), # FIXME + Key('ToDoList', 'To Do List'), Key('ClearGrab', 'Clear Grab'), Key('Close'), Key('Copy'), @@ -455,7 +455,7 @@ KEYS = [ Key('Option'), Key('Paste'), Key('Phone'), - Key('Calendar', '\u17c0\udce4'), # FIXME + Key('Calendar'), Key('Reply'), Key('Reload'), Key('RotateWindows', 'Rotate Windows'), @@ -496,10 +496,10 @@ KEYS = [ Key('TopMenu', 'Top Menu'), Key('PowerDown', 'Power Down'), Key('Suspend'), - Key('ContrastAdjust', '\u17c0\udd0d'), # FIXME + Key('ContrastAdjust', 'Contrast Adjust'), - Key('LaunchG', '\u17c0\udd0e'), # FIXME - Key('LaunchH', '\u17c0\udd0f'), # FIXME + Key('LaunchG', 'Launch (G)'), + Key('LaunchH', 'Launch (H)'), Key('TouchpadToggle', 'Touchpad Toggle'), Key('TouchpadOn', 'Touchpad On'), @@ -528,7 +528,7 @@ KEYS = [ Key('Undo'), Key('Redo'), - Key('MediaLast', '\u17ff\udfff'), # FIXME + Key('MediaLast', 'Media Last'), ### Keypad navigation keys Key('Select'), @@ -562,5 +562,5 @@ KEYS = [ Key('Camera', 'Camera Shutter'), Key('CameraFocus', 'Camera Focus'), - Key('unknown', ''), # FIXME + Key('unknown', 'Unknown'), ] diff --git a/tests/unit/keyinput/test_keyutils.py b/tests/unit/keyinput/test_keyutils.py index e5b6268e5..1e8bdfb74 100644 --- a/tests/unit/keyinput/test_keyutils.py +++ b/tests/unit/keyinput/test_keyutils.py @@ -35,43 +35,26 @@ def qt_key(request): return key -def test_new_to_string(qt_key): - name = qt_key.attribute if qt_key.name is None else qt_key.name - assert keyutils._key_to_string(qt_key.member) == name - - class TestKeyToString: - @pytest.mark.parametrize('key, expected', [ - (Qt.Key_Blue, 'Blue'), - (Qt.Key_Backtab, 'Tab'), - (Qt.Key_Escape, 'Escape'), - (Qt.Key_A, 'A'), - (Qt.Key_degree, '°'), - (Qt.Key_Meta, 'Meta'), - ]) - @pytest.mark.skipif(True, reason='FIXME') - def test_normal(self, key, expected): - """Test a special key where QKeyEvent::toString works incorrectly.""" - assert keyutils._key_to_string(key) == expected + def test_to_string(self, qt_key): + name = qt_key.attribute if qt_key.name is None else qt_key.name + assert keyutils._key_to_string(qt_key.member) == name def test_missing(self, monkeypatch): - """Test with a missing key.""" monkeypatch.delattr(keyutils.Qt, 'Key_Blue') # We don't want to test the key which is actually missing - we only # want to know if the mapping still behaves properly. assert keyutils._key_to_string(Qt.Key_A) == 'A' - @pytest.mark.skipif(True, reason='FIXME') def test_all(self): - """Make sure there's some sensible output for all keys.""" - for name, value in sorted(vars(Qt).items()): - if not isinstance(value, Qt.Key): - continue - print(name) - string = keyutils._key_to_string(value) - assert string - string.encode('utf-8') # make sure it's encodable + """Make sure all possible keys are in key_data.KEYS.""" + key_names = {name[len("Key_"):] + for name, value in sorted(vars(Qt).items()) + if isinstance(value, Qt.Key)} + key_data_names = {key.attribute for key in sorted(key_data.KEYS)} + diff = key_names - key_data_names + assert not diff class TestKeyEventToString: