Merge branch 'rcorre-better_keyhints'
This commit is contained in:
commit
a6abc86456
@ -31,7 +31,7 @@ Added
|
|||||||
terminal
|
terminal
|
||||||
- New `:messages` command to show error messages
|
- New `:messages` command to show error messages
|
||||||
- New pop-up showing possible keybinding when the first key of a keychain is
|
- New pop-up showing possible keybinding when the first key of a keychain is
|
||||||
pressed. This can be turned off using `:set ui show-keyhints false`.
|
pressed. This can be turned off using `:set ui keyhint-blacklist *`.
|
||||||
|
|
||||||
Changed
|
Changed
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
@ -141,8 +141,8 @@ Contributors, sorted by the number of commits in descending order:
|
|||||||
* Daniel Schadt
|
* Daniel Schadt
|
||||||
* Antoni Boucher
|
* Antoni Boucher
|
||||||
* Lamar Pavel
|
* Lamar Pavel
|
||||||
* Bruno Oliveira
|
|
||||||
* Ryan Roden-Corrent
|
* Ryan Roden-Corrent
|
||||||
|
* Bruno Oliveira
|
||||||
* Alexander Cogneau
|
* Alexander Cogneau
|
||||||
* Felix Van der Jeugt
|
* Felix Van der Jeugt
|
||||||
* Martin Tournoij
|
* Martin Tournoij
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
|<<ui-hide-mouse-cursor,hide-mouse-cursor>>|Whether to hide the mouse cursor.
|
|<<ui-hide-mouse-cursor,hide-mouse-cursor>>|Whether to hide the mouse cursor.
|
||||||
|<<ui-modal-js-dialog,modal-js-dialog>>|Use standard JavaScript modal dialog for alert() and confirm()
|
|<<ui-modal-js-dialog,modal-js-dialog>>|Use standard JavaScript modal dialog for alert() and confirm()
|
||||||
|<<ui-hide-wayland-decoration,hide-wayland-decoration>>|Hide the window decoration when using wayland (requires restart)
|
|<<ui-hide-wayland-decoration,hide-wayland-decoration>>|Hide the window decoration when using wayland (requires restart)
|
||||||
|<<ui-show-keyhints,show-keyhints>>|Show possible keychains based on the current keystring
|
|<<ui-keyhint-blacklist,keyhint-blacklist>>|Keychains that shouldn't be shown in the keyhint dialog
|
||||||
|==============
|
|==============
|
||||||
|
|
||||||
.Quick reference for section ``network''
|
.Quick reference for section ``network''
|
||||||
@ -671,16 +671,13 @@ Valid values:
|
|||||||
|
|
||||||
Default: +pass:[false]+
|
Default: +pass:[false]+
|
||||||
|
|
||||||
[[ui-show-keyhints]]
|
[[ui-keyhint-blacklist]]
|
||||||
=== show-keyhints
|
=== keyhint-blacklist
|
||||||
Show possible keychains based on the current keystring
|
Keychains that shouldn't be shown in the keyhint dialog
|
||||||
|
|
||||||
Valid values:
|
Globs are supported, so ';*' will blacklist all keychainsstarting with ';'. Use '*' to disable keyhints
|
||||||
|
|
||||||
* +true+
|
Default: empty
|
||||||
* +false+
|
|
||||||
|
|
||||||
Default: +pass:[true]+
|
|
||||||
|
|
||||||
== network
|
== network
|
||||||
Settings related to the network.
|
Settings related to the network.
|
||||||
|
@ -341,6 +341,7 @@ class ConfigManager(QObject):
|
|||||||
('colors', 'tab.indicator.system'): 'tabs.indicator.system',
|
('colors', 'tab.indicator.system'): 'tabs.indicator.system',
|
||||||
('completion', 'history-length'): 'cmd-history-max-items',
|
('completion', 'history-length'): 'cmd-history-max-items',
|
||||||
('colors', 'downloads.fg'): 'downloads.fg.start',
|
('colors', 'downloads.fg'): 'downloads.fg.start',
|
||||||
|
('ui', 'show-keyhints'): 'keyhint-blacklist',
|
||||||
}
|
}
|
||||||
DELETED_OPTIONS = [
|
DELETED_OPTIONS = [
|
||||||
('colors', 'tab.separator'),
|
('colors', 'tab.separator'),
|
||||||
@ -360,6 +361,8 @@ class ConfigManager(QObject):
|
|||||||
_get_value_transformer({'false': '-1', 'true': '1000'}),
|
_get_value_transformer({'false': '-1', 'true': '1000'}),
|
||||||
('general', 'log-javascript-console'):
|
('general', 'log-javascript-console'):
|
||||||
_get_value_transformer({'false': 'none', 'true': 'debug'}),
|
_get_value_transformer({'false': 'none', 'true': 'debug'}),
|
||||||
|
('ui', 'keyhint-blacklist'):
|
||||||
|
_get_value_transformer({'false': '*', 'true': ''}),
|
||||||
}
|
}
|
||||||
|
|
||||||
changed = pyqtSignal(str, str)
|
changed = pyqtSignal(str, str)
|
||||||
|
@ -355,9 +355,11 @@ def data(readonly=False):
|
|||||||
"Hide the window decoration when using wayland "
|
"Hide the window decoration when using wayland "
|
||||||
"(requires restart)"),
|
"(requires restart)"),
|
||||||
|
|
||||||
('show-keyhints',
|
('keyhint-blacklist',
|
||||||
SettingValue(typ.Bool(), 'true'),
|
SettingValue(typ.List(none_ok=True), ''),
|
||||||
"Show possible keychains based on the current keystring"),
|
"Keychains that shouldn't be shown in the keyhint dialog\n\n"
|
||||||
|
"Globs are supported, so ';*' will blacklist all keychains"
|
||||||
|
"starting with ';'. Use '*' to disable keyhints"),
|
||||||
|
|
||||||
readonly=readonly
|
readonly=readonly
|
||||||
)),
|
)),
|
||||||
|
@ -25,12 +25,13 @@ It is intended to help discoverability of keybindings.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import html
|
import html
|
||||||
|
import fnmatch
|
||||||
|
|
||||||
from PyQt5.QtWidgets import QLabel, QSizePolicy
|
from PyQt5.QtWidgets import QLabel, QSizePolicy
|
||||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt
|
from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt
|
||||||
|
|
||||||
from qutebrowser.config import config, style
|
from qutebrowser.config import config, style
|
||||||
from qutebrowser.utils import objreg, utils
|
from qutebrowser.utils import objreg, utils, usertypes
|
||||||
|
|
||||||
|
|
||||||
class KeyHintView(QLabel):
|
class KeyHintView(QLabel):
|
||||||
@ -39,7 +40,6 @@ class KeyHintView(QLabel):
|
|||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
_win_id: Window ID of parent.
|
_win_id: Window ID of parent.
|
||||||
_enabled: If False, do not show the window at all
|
|
||||||
|
|
||||||
Signals:
|
Signals:
|
||||||
reposition_keyhint: Emitted when this widget should be resized.
|
reposition_keyhint: Emitted when this widget should be resized.
|
||||||
@ -61,23 +61,16 @@ class KeyHintView(QLabel):
|
|||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setTextFormat(Qt.RichText)
|
self.setTextFormat(Qt.RichText)
|
||||||
self._win_id = win_id
|
self._win_id = win_id
|
||||||
self.set_enabled()
|
|
||||||
cfg = objreg.get('config')
|
|
||||||
cfg.changed.connect(self.set_enabled)
|
|
||||||
style.set_register_stylesheet(self)
|
style.set_register_stylesheet(self)
|
||||||
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Minimum)
|
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Minimum)
|
||||||
self.hide()
|
self.hide()
|
||||||
|
self._show_timer = usertypes.Timer(self, 'keyhint_show')
|
||||||
|
self._show_timer.setInterval(500)
|
||||||
|
self._show_timer.timeout.connect(self.show)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return utils.get_repr(self, win_id=self._win_id)
|
return utils.get_repr(self, win_id=self._win_id)
|
||||||
|
|
||||||
@config.change_filter('ui', 'show-keyhints')
|
|
||||||
def set_enabled(self):
|
|
||||||
"""Update self._enabled when the config changed."""
|
|
||||||
self._enabled = config.get('ui', 'show-keyhints')
|
|
||||||
if not self._enabled:
|
|
||||||
self.hide()
|
|
||||||
|
|
||||||
def showEvent(self, e):
|
def showEvent(self, e):
|
||||||
"""Adjust the keyhint size when it's freshly shown."""
|
"""Adjust the keyhint size when it's freshly shown."""
|
||||||
self.reposition_keyhint.emit()
|
self.reposition_keyhint.emit()
|
||||||
@ -90,19 +83,29 @@ class KeyHintView(QLabel):
|
|||||||
Args:
|
Args:
|
||||||
prefix: The current partial keystring.
|
prefix: The current partial keystring.
|
||||||
"""
|
"""
|
||||||
if not prefix or not self._enabled:
|
if not prefix:
|
||||||
|
self._show_timer.stop()
|
||||||
self.hide()
|
self.hide()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
blacklist = config.get('ui', 'keyhint-blacklist') or []
|
||||||
keyconf = objreg.get('key-config')
|
keyconf = objreg.get('key-config')
|
||||||
|
|
||||||
|
def blacklisted(keychain):
|
||||||
|
return any(fnmatch.fnmatchcase(keychain, glob)
|
||||||
|
for glob in blacklist)
|
||||||
|
|
||||||
bindings = [(k, v) for (k, v)
|
bindings = [(k, v) for (k, v)
|
||||||
in keyconf.get_bindings_for(modename).items()
|
in keyconf.get_bindings_for(modename).items()
|
||||||
if k.startswith(prefix) and not utils.is_special_key(k)]
|
if k.startswith(prefix) and not utils.is_special_key(k) and
|
||||||
|
not blacklisted(k)]
|
||||||
|
|
||||||
if not bindings:
|
if not bindings:
|
||||||
|
self._show_timer.stop()
|
||||||
return
|
return
|
||||||
|
|
||||||
self.show()
|
# delay so a quickly typed keychain doesn't display hints
|
||||||
|
self._show_timer.start()
|
||||||
suffix_color = html.escape(config.get('colors', 'keyhint.fg.suffix'))
|
suffix_color = html.escape(config.get('colors', 'keyhint.fg.suffix'))
|
||||||
|
|
||||||
text = ''
|
text = ''
|
||||||
|
@ -34,7 +34,6 @@ def expected_text(*args):
|
|||||||
"""
|
"""
|
||||||
text = '<table>'
|
text = '<table>'
|
||||||
for group in args:
|
for group in args:
|
||||||
print("group = {}".format(group))
|
|
||||||
text += ("<tr>"
|
text += ("<tr>"
|
||||||
"<td>{}</td>"
|
"<td>{}</td>"
|
||||||
"<td style='color: {}'>{}</td>"
|
"<td style='color: {}'>{}</td>"
|
||||||
@ -54,7 +53,7 @@ def keyhint(qtbot, config_stub, key_config_stub):
|
|||||||
'keyhint.bg': 'black'
|
'keyhint.bg': 'black'
|
||||||
},
|
},
|
||||||
'fonts': {'keyhint': 'Comic Sans'},
|
'fonts': {'keyhint': 'Comic Sans'},
|
||||||
'ui': {'show-keyhints': True},
|
'ui': {'keyhint-blacklist': ''},
|
||||||
}
|
}
|
||||||
keyhint = KeyHintView(0, None)
|
keyhint = KeyHintView(0, None)
|
||||||
qtbot.add_widget(keyhint)
|
qtbot.add_widget(keyhint)
|
||||||
@ -63,7 +62,7 @@ def keyhint(qtbot, config_stub, key_config_stub):
|
|||||||
|
|
||||||
|
|
||||||
def test_suggestions(keyhint, key_config_stub):
|
def test_suggestions(keyhint, key_config_stub):
|
||||||
"""Test cursor position based on the prompt."""
|
"""Test that keyhints are shown based on a prefix."""
|
||||||
# we want the dict to return sorted items() for reliable testing
|
# we want the dict to return sorted items() for reliable testing
|
||||||
key_config_stub.set_bindings_for('normal', OrderedDict([
|
key_config_stub.set_bindings_for('normal', OrderedDict([
|
||||||
('aa', 'cmd-aa'),
|
('aa', 'cmd-aa'),
|
||||||
@ -95,14 +94,6 @@ def test_special_bindings(keyhint, key_config_stub):
|
|||||||
('<', 'yellow', 'b', 'cmd-<b'))
|
('<', 'yellow', 'b', 'cmd-<b'))
|
||||||
|
|
||||||
|
|
||||||
def test_disable(keyhint, config_stub):
|
|
||||||
"""Ensure the widget isn't visible if disabled."""
|
|
||||||
config_stub.set('ui', 'show-keyhints', False)
|
|
||||||
keyhint.update_keyhint('normal', 'a')
|
|
||||||
assert not keyhint.text()
|
|
||||||
assert not keyhint.isVisible()
|
|
||||||
|
|
||||||
|
|
||||||
def test_color_switch(keyhint, config_stub, key_config_stub):
|
def test_color_switch(keyhint, config_stub, key_config_stub):
|
||||||
"""Ensure the the keyhint suffix color can be updated at runtime."""
|
"""Ensure the the keyhint suffix color can be updated at runtime."""
|
||||||
config_stub.set('colors', 'keyhint.fg.suffix', '#ABCDEF')
|
config_stub.set('colors', 'keyhint.fg.suffix', '#ABCDEF')
|
||||||
@ -118,4 +109,37 @@ def test_no_matches(keyhint, key_config_stub):
|
|||||||
('aa', 'cmd-aa'),
|
('aa', 'cmd-aa'),
|
||||||
('ab', 'cmd-ab')]))
|
('ab', 'cmd-ab')]))
|
||||||
keyhint.update_keyhint('normal', 'z')
|
keyhint.update_keyhint('normal', 'z')
|
||||||
|
assert not keyhint.text()
|
||||||
assert not keyhint.isVisible()
|
assert not keyhint.isVisible()
|
||||||
|
|
||||||
|
|
||||||
|
def test_blacklist(keyhint, config_stub, key_config_stub):
|
||||||
|
"""Test that blacklisted keychaints aren't hinted."""
|
||||||
|
config_stub.set('ui', 'keyhint-blacklist', ['ab*'])
|
||||||
|
# we want the dict to return sorted items() for reliable testing
|
||||||
|
key_config_stub.set_bindings_for('normal', OrderedDict([
|
||||||
|
('aa', 'cmd-aa'),
|
||||||
|
('ab', 'cmd-ab'),
|
||||||
|
('aba', 'cmd-aba'),
|
||||||
|
('abb', 'cmd-abb'),
|
||||||
|
('xd', 'cmd-xd'),
|
||||||
|
('xe', 'cmd-xe')]))
|
||||||
|
|
||||||
|
keyhint.update_keyhint('normal', 'a')
|
||||||
|
assert keyhint.text() == expected_text(('a', 'yellow', 'a', 'cmd-aa'))
|
||||||
|
|
||||||
|
|
||||||
|
def test_blacklist_all(keyhint, config_stub, key_config_stub):
|
||||||
|
"""Test that setting the blacklist to * disables keyhints."""
|
||||||
|
config_stub.set('ui', 'keyhint-blacklist', ['*'])
|
||||||
|
# we want the dict to return sorted items() for reliable testing
|
||||||
|
key_config_stub.set_bindings_for('normal', OrderedDict([
|
||||||
|
('aa', 'cmd-aa'),
|
||||||
|
('ab', 'cmd-ab'),
|
||||||
|
('aba', 'cmd-aba'),
|
||||||
|
('abb', 'cmd-abb'),
|
||||||
|
('xd', 'cmd-xd'),
|
||||||
|
('xe', 'cmd-xe')]))
|
||||||
|
|
||||||
|
keyhint.update_keyhint('normal', 'a')
|
||||||
|
assert not keyhint.text()
|
||||||
|
Loading…
Reference in New Issue
Block a user