Show possible keychains from current input.
When the current keystring is a partial match for one or more bindings, show the possible bindings in a small overlay. The overlay is partially transparent by default, but the background color is configurable as ui->keystring.bg.
This commit is contained in:
parent
d03659b3da
commit
8406746889
@ -355,6 +355,10 @@ def data(readonly=False):
|
||||
"Hide the window decoration when using wayland "
|
||||
"(requires restart)"),
|
||||
|
||||
('show-keyhints',
|
||||
SettingValue(typ.Bool(), 'true'),
|
||||
"Show possible keychains based on the current keystring"),
|
||||
|
||||
readonly=readonly
|
||||
)),
|
||||
|
||||
@ -1196,6 +1200,18 @@ def data(readonly=False):
|
||||
"Background color for webpages if unset (or empty to use the "
|
||||
"theme's color)"),
|
||||
|
||||
('keyhint.fg',
|
||||
SettingValue(typ.QssColor(), '#FFFFFF'),
|
||||
"Text color for the keyhint widget."),
|
||||
|
||||
('keyhint.fg.suffix',
|
||||
SettingValue(typ.QssColor(), '#FFFF00'),
|
||||
"Highlight color for keys to complete the current keychain"),
|
||||
|
||||
('keyhint.bg',
|
||||
SettingValue(typ.QssColor(), 'rgba(0, 0, 0, 80%)'),
|
||||
"Background color of the keyhint widget."),
|
||||
|
||||
readonly=readonly
|
||||
)),
|
||||
|
||||
@ -1277,6 +1293,10 @@ def data(readonly=False):
|
||||
typ.Int(none_ok=True, minval=1, maxval=MAXVALS['int']), ''),
|
||||
"The default font size for fixed-pitch text."),
|
||||
|
||||
('keyhint',
|
||||
SettingValue(typ.Font(), DEFAULT_FONT_SIZE + ' ${_monospace}'),
|
||||
"Font used in the keyhint widget."),
|
||||
|
||||
readonly=readonly
|
||||
)),
|
||||
])
|
||||
|
@ -35,7 +35,7 @@ from qutebrowser.mainwindow.statusbar import bar
|
||||
from qutebrowser.completion import completionwidget
|
||||
from qutebrowser.keyinput import modeman
|
||||
from qutebrowser.browser import hints, downloads, downloadview, commands
|
||||
from qutebrowser.misc import crashsignal
|
||||
from qutebrowser.misc import crashsignal, keyhintwidget
|
||||
|
||||
|
||||
win_id_gen = itertools.count(0)
|
||||
@ -160,6 +160,8 @@ class MainWindow(QWidget):
|
||||
|
||||
self._commandrunner = runners.CommandRunner(self.win_id)
|
||||
|
||||
self._keyhint = keyhintwidget.KeyHintView(self.win_id, self)
|
||||
|
||||
log.init.debug("Initializing modes...")
|
||||
modeman.init(self.win_id, self)
|
||||
|
||||
@ -178,6 +180,7 @@ class MainWindow(QWidget):
|
||||
# resizing will fail. Therefore, we use singleShot QTimers to make sure
|
||||
# we defer this until everything else is initialized.
|
||||
QTimer.singleShot(0, self._connect_resize_completion)
|
||||
QTimer.singleShot(0, self._connect_resize_keyhint)
|
||||
objreg.get('config').changed.connect(self.on_config_changed)
|
||||
|
||||
if config.get('ui', 'hide-mouse-cursor'):
|
||||
@ -252,6 +255,11 @@ class MainWindow(QWidget):
|
||||
self._completion.resize_completion.connect(self.resize_completion)
|
||||
self.resize_completion()
|
||||
|
||||
def _connect_resize_keyhint(self):
|
||||
"""Connect the reposition_keyhint signal and resize it once."""
|
||||
self._keyhint.reposition_keyhint.connect(self.reposition_keyhint)
|
||||
self.reposition_keyhint()
|
||||
|
||||
def _set_default_geometry(self):
|
||||
"""Set some sensible default geometry."""
|
||||
self.setGeometry(QRect(50, 50, 800, 600))
|
||||
@ -286,6 +294,8 @@ class MainWindow(QWidget):
|
||||
# commands
|
||||
keyparsers[usertypes.KeyMode.normal].keystring_updated.connect(
|
||||
status.keystring.setText)
|
||||
keyparsers[usertypes.KeyMode.normal].keystring_updated.connect(
|
||||
self._keyhint.update_keyhint)
|
||||
cmd.got_cmd.connect(self._commandrunner.run_safely)
|
||||
cmd.returnPressed.connect(tabs.on_cmd_return_pressed)
|
||||
tabs.got_cmd.connect(self._commandrunner.run_safely)
|
||||
@ -367,6 +377,24 @@ class MainWindow(QWidget):
|
||||
if rect.isValid():
|
||||
self._completion.setGeometry(rect)
|
||||
|
||||
@pyqtSlot()
|
||||
def reposition_keyhint(self):
|
||||
"""Adjust keyhint according to config."""
|
||||
if not self._keyhint.isVisible():
|
||||
return
|
||||
# Shrink the window to the shown text and place it at the bottom left
|
||||
width = self._keyhint.width()
|
||||
height = self._keyhint.height()
|
||||
topleft_y = self.height() - self.status.height() - height
|
||||
topleft_y = qtutils.check_overflow(topleft_y, 'int', fatal=False)
|
||||
topleft = QPoint(0, topleft_y)
|
||||
bottomright = (self.status.geometry().topLeft() +
|
||||
QPoint(width, 0))
|
||||
rect = QRect(topleft, bottomright)
|
||||
log.misc.debug('keyhint rect: {}'.format(rect))
|
||||
if rect.isValid():
|
||||
self._keyhint.setGeometry(rect)
|
||||
|
||||
@cmdutils.register(instance='main-window', scope='window')
|
||||
@pyqtSlot()
|
||||
def close(self):
|
||||
@ -394,6 +422,7 @@ class MainWindow(QWidget):
|
||||
"""
|
||||
super().resizeEvent(e)
|
||||
self.resize_completion()
|
||||
self.reposition_keyhint()
|
||||
self._downloadview.updateGeometry()
|
||||
self.tabbed_browser.tabBar().refresh()
|
||||
|
||||
|
106
qutebrowser/misc/keyhintwidget.py
Normal file
106
qutebrowser/misc/keyhintwidget.py
Normal file
@ -0,0 +1,106 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016 Ryan Roden-Corrent (rcorre) <ryan@rcorre.net>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Small window that pops up to show hints for possible keystrings.
|
||||
|
||||
When a user inputs a key that forms a partial match, this shows a small window
|
||||
with each possible completion of that keystring and the corresponding command.
|
||||
It is intended to help discoverability of keybindings.
|
||||
"""
|
||||
|
||||
from PyQt5.QtWidgets import QLabel, QSizePolicy
|
||||
from PyQt5.QtCore import pyqtSlot, pyqtSignal
|
||||
|
||||
from qutebrowser.config import config, style
|
||||
from qutebrowser.utils import objreg, utils
|
||||
|
||||
|
||||
class KeyHintView(QLabel):
|
||||
|
||||
"""The view showing hints for key bindings based on the current key string.
|
||||
|
||||
Attributes:
|
||||
_win_id: Window ID of parent.
|
||||
_enabled: If False, do not show the window at all
|
||||
_suffix_color: Highlight for completions to the current keychain.
|
||||
|
||||
Signals:
|
||||
reposition_keyhint: Emitted when this widget should be resized.
|
||||
"""
|
||||
|
||||
STYLESHEET = """
|
||||
QLabel {
|
||||
font: {{ font['keyhint'] }};
|
||||
color: {{ color['keyhint.fg'] }};
|
||||
background-color: {{ color['keyhint.bg'] }};
|
||||
}
|
||||
"""
|
||||
|
||||
reposition_keyhint = pyqtSignal()
|
||||
|
||||
def __init__(self, win_id, parent=None):
|
||||
super().__init__(parent)
|
||||
self._win_id = win_id
|
||||
self.set_enabled()
|
||||
config = objreg.get('config')
|
||||
config.changed.connect(self.set_enabled)
|
||||
self._suffix_color = config.get('colors', 'keyhint.fg.suffix')
|
||||
style.set_register_stylesheet(self)
|
||||
self.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Minimum)
|
||||
self.setVisible(False)
|
||||
|
||||
def __repr__(self):
|
||||
return utils.get_repr(self)
|
||||
|
||||
@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.setVisible(False)
|
||||
|
||||
def showEvent(self, e):
|
||||
"""Adjust the keyhint size when it's freshly shown."""
|
||||
self.reposition_keyhint.emit()
|
||||
super().showEvent(e)
|
||||
|
||||
@pyqtSlot(str)
|
||||
def update_keyhint(self, prefix):
|
||||
"""Show hints for the given prefix (or hide if prefix is empty).
|
||||
|
||||
Args:
|
||||
prefix: The current partial keystring.
|
||||
"""
|
||||
if len(prefix) == 0 or not self._enabled:
|
||||
self.setVisible(False)
|
||||
return
|
||||
|
||||
self.setVisible(True)
|
||||
|
||||
text = ''
|
||||
keyconf = objreg.get('key-config')
|
||||
# this is only fired in normal mode
|
||||
for key, cmd in keyconf.get_bindings_for('normal').items():
|
||||
if key.startswith(prefix):
|
||||
suffix = "<font color={}>{}</font>".format(self._suffix_color,
|
||||
key[len(prefix):])
|
||||
text += '{}{}\t<b>{}</b><br>'.format(prefix, suffix, cmd)
|
||||
|
||||
self.setText(text)
|
||||
self.adjustSize()
|
||||
self.reposition_keyhint.emit()
|
Loading…
Reference in New Issue
Block a user