Implement modes and hint input.

This commit is contained in:
Florian Bruhin 2014-04-21 15:20:41 +02:00
parent 5a3966ca82
commit d7b87e09c9
5 changed files with 136 additions and 34 deletions

View File

@ -57,6 +57,7 @@ from qutebrowser.widgets.mainwindow import MainWindow
from qutebrowser.widgets.crash import CrashDialog from qutebrowser.widgets.crash import CrashDialog
from qutebrowser.commands.keys import CommandKeyParser from qutebrowser.commands.keys import CommandKeyParser
from qutebrowser.commands.parsers import CommandParser, SearchParser from qutebrowser.commands.parsers import CommandParser, SearchParser
from qutebrowser.browser.hints import HintKeyParser
from qutebrowser.utils.appdirs import AppDirs from qutebrowser.utils.appdirs import AppDirs
from qutebrowser.utils.misc import dotted_getattr from qutebrowser.utils.misc import dotted_getattr
from qutebrowser.utils.debug import set_trace # pylint: disable=unused-import from qutebrowser.utils.debug import set_trace # pylint: disable=unused-import
@ -74,13 +75,14 @@ class QuteBrowser(QApplication):
Attributes: Attributes:
mainwindow: The MainWindow QWidget. mainwindow: The MainWindow QWidget.
commandparser: The main CommandParser instance. commandparser: The main CommandParser instance.
keyparser: The main CommandKeyParser instance.
searchparser: The main SearchParser instance. searchparser: The main SearchParser instance.
_keyparsers: A mapping from modes to keyparsers.
_dirs: AppDirs instance for config/cache directories. _dirs: AppDirs instance for config/cache directories.
_args: ArgumentParser instance. _args: ArgumentParser instance.
_timers: List of used QTimers so they don't get GCed. _timers: List of used QTimers so they don't get GCed.
_shutting_down: True if we're currently shutting down. _shutting_down: True if we're currently shutting down.
_quit_status: The current quitting status. _quit_status: The current quitting status.
_mode: The mode we're currently in.
""" """
def __init__(self): def __init__(self):
@ -88,6 +90,7 @@ class QuteBrowser(QApplication):
self._quit_status = {} self._quit_status = {}
self._timers = [] self._timers = []
self._shutting_down = False self._shutting_down = False
self._mode = None
sys.excepthook = self._exception_hook sys.excepthook = self._exception_hook
@ -108,40 +111,16 @@ class QuteBrowser(QApplication):
self.commandparser = CommandParser() self.commandparser = CommandParser()
self.searchparser = SearchParser() self.searchparser = SearchParser()
self.keyparser = CommandKeyParser(self) self._keyparsers = {
"normal": CommandKeyParser(self),
"hint": HintKeyParser(self),
}
self._init_cmds() self._init_cmds()
self.mainwindow = MainWindow() self.mainwindow = MainWindow()
self.setQuitOnLastWindowClosed(False) self.setQuitOnLastWindowClosed(False)
self.lastWindowClosed.connect(self.shutdown)
self.mainwindow.tabs.keypress.connect( self._connect_signals()
self.mainwindow.status.keypress) self.set_mode("normal")
self.mainwindow.tabs.keypress.connect(self.keyparser.handle)
self.keyparser.set_cmd_text.connect(
self.mainwindow.status.cmd.set_cmd_text)
self.mainwindow.tabs.set_cmd_text.connect(
self.mainwindow.status.cmd.set_cmd_text)
self.mainwindow.tabs.quit.connect(self.shutdown)
self.mainwindow.status.cmd.got_cmd.connect(self.commandparser.run)
self.mainwindow.status.cmd.got_search.connect(self.searchparser.search)
self.mainwindow.status.cmd.got_search_rev.connect(
self.searchparser.search_rev)
self.mainwindow.status.cmd.returnPressed.connect(
self.mainwindow.tabs.setFocus)
self.searchparser.do_search.connect(
self.mainwindow.tabs.cur.search)
self.keyparser.keystring_updated.connect(
self.mainwindow.status.keystring.setText)
message.bridge.error.connect(self.mainwindow.status.disp_error)
message.bridge.info.connect(self.mainwindow.status.disp_tmp_text)
self.config.style_changed.connect(style.invalidate_caches)
self.config.changed.connect(self.mainwindow.tabs.on_config_changed)
self.config.changed.connect(
self.mainwindow.completion.on_config_changed)
self.config.changed.connect(self.mainwindow.on_config_changed)
self.config.changed.connect(config.cmd_history.on_config_changed)
self.config.changed.connect(websettings.on_config_changed)
self.config.changed.connect(self.keyparser.on_config_changed)
self.mainwindow.show() self.mainwindow.show()
self._python_hacks() self._python_hacks()
@ -241,6 +220,45 @@ class QuteBrowser(QApplication):
timer.timeout.connect(lambda: None) timer.timeout.connect(lambda: None)
self._timers.append(timer) self._timers.append(timer)
def _connect_signals(self):
"""Connect all signals to their slots."""
self.lastWindowClosed.connect(self.shutdown)
self.mainwindow.tabs.keypress.connect(
self.mainwindow.status.keypress)
self._keyparsers["normal"].set_cmd_text.connect(
self.mainwindow.status.cmd.set_cmd_text)
self.mainwindow.tabs.set_cmd_text.connect(
self.mainwindow.status.cmd.set_cmd_text)
self.mainwindow.tabs.quit.connect(self.shutdown)
self.mainwindow.status.cmd.got_cmd.connect(self.commandparser.run)
self.mainwindow.status.cmd.got_search.connect(self.searchparser.search)
self.mainwindow.status.cmd.got_search_rev.connect(
self.searchparser.search_rev)
self.mainwindow.status.cmd.returnPressed.connect(
self.mainwindow.tabs.setFocus)
self.searchparser.do_search.connect(
self.mainwindow.tabs.cur.search)
self._keyparsers["normal"].keystring_updated.connect(
self.mainwindow.status.keystring.setText)
self._keyparsers["hint"].fire_hint.connect(
self.mainwindow.tabs.cur.fire_hint)
self._keyparsers["hint"].keystring_updated.connect(
self.mainwindow.tabs.cur.handle_hint_key)
self.mainwindow.tabs.hint_strings_updated.connect(
self._keyparsers["hint"].on_hint_strings_updated)
self.mainwindow.tabs.set_mode.connect(self.set_mode)
message.bridge.error.connect(self.mainwindow.status.disp_error)
message.bridge.info.connect(self.mainwindow.status.disp_tmp_text)
self.config.style_changed.connect(style.invalidate_caches)
self.config.changed.connect(self.mainwindow.tabs.on_config_changed)
self.config.changed.connect(
self.mainwindow.completion.on_config_changed)
self.config.changed.connect(self.mainwindow.on_config_changed)
self.config.changed.connect(config.cmd_history.on_config_changed)
self.config.changed.connect(websettings.on_config_changed)
self.config.changed.connect(
self._keyparsers["normal"].on_config_changed)
def _recover_pages(self): def _recover_pages(self):
"""Try to recover all open pages. """Try to recover all open pages.
@ -340,6 +358,20 @@ class QuteBrowser(QApplication):
logging.debug("maybe_quit quitting.") logging.debug("maybe_quit quitting.")
self.quit() self.quit()
@pyqtSlot(str)
def set_mode(self, mode):
"""Set a key input mode.
Args:
mode: The new mode to set, as an index for self._keyparsers.
"""
if self._mode is not None:
oldhandler = self._keyparsers[self._mode]
self.mainwindow.tabs.keypress.disconnect(oldhandler.handle)
handler = self._keyparsers[mode]
self.mainwindow.tabs.keypress.connect(handler.handle)
self._mode = mode
@cmdutils.register(instance='', maxsplit=0) @cmdutils.register(instance='', maxsplit=0)
def pyeval(self, s): def pyeval(self, s):
"""Evaluate a python string and display the results as a webpage. """Evaluate a python string and display the results as a webpage.

View File

@ -175,6 +175,16 @@ class CurCommandDispatcher(QObject):
""" """
self._tabs.currentWidget().hintmanager.start(mode) self._tabs.currentWidget().hintmanager.start(mode)
@pyqtSlot(str)
def handle_hint_key(self, keystr):
"""Handle a new hint keypress."""
self._tabs.currentWidget().hintmanager.handle_partial_key(keystr)
@pyqtSlot(str)
def fire_hint(self, keystr):
"""Fire a completed hint."""
self._tabs.currentWidget().hintmanager.fire(keystr)
@pyqtSlot(str, int) @pyqtSlot(str, int)
def search(self, text, flags): def search(self, text, flags):
"""Search for text in the current page. """Search for text in the current page.

View File

@ -19,10 +19,41 @@
import math import math
from PyQt5.QtCore import pyqtSignal, QObject
import qutebrowser.config.config as config import qutebrowser.config.config as config
from qutebrowser.utils.keyparser import KeyParser
class HintManager: class HintKeyParser(KeyParser):
"""KeyParser for hints.
Class attributes:
supports_count: If the keyparser should support counts.
Signals:
fire_hint: When a hint keybinding was completed.
Arg: the keystring/hint string pressed.
"""
supports_count = False
fire_hint = pyqtSignal(str)
def execute(self, cmdstr, count=None):
"""Handle a completed keychain."""
self.fire_hint.emit(cmdstr)
def on_hint_strings_updated(self, strings):
"""Handler for HintManager's hint_strings_updated.
Args:
strings: A list of hint strings.
"""
self.bindings = {s: s for s in strings}
class HintManager(QObject):
"""Manage drawing hints over links or other elements. """Manage drawing hints over links or other elements.
@ -34,6 +65,10 @@ class HintManager:
_frame: The QWebFrame to use. _frame: The QWebFrame to use.
_elems: The elements we're hinting currently. _elems: The elements we're hinting currently.
_labels: The label elements. _labels: The label elements.
Signals:
hint_strings_updated: Emitted when the possible hint strings changed.
set_mode: Emitted when the input mode should be changed.
""" """
SELECTORS = { SELECTORS = {
@ -60,12 +95,16 @@ class HintManager:
top: {top}px; top: {top}px;
""" """
hint_strings_updated = pyqtSignal(list)
set_mode = pyqtSignal(str)
def __init__(self, frame): def __init__(self, frame):
"""Constructor. """Constructor.
Args: Args:
frame: The QWebFrame to use for finding elements and drawing. frame: The QWebFrame to use for finding elements and drawing.
""" """
super().__init__(frame)
self._frame = frame self._frame = frame
self._elems = [] self._elems = []
self._labels = [] self._labels = []
@ -196,6 +235,8 @@ class HintManager:
strings = self._hint_strings(self._elems) strings = self._hint_strings(self._elems)
for e, string in zip(self._elems, strings): for e, string in zip(self._elems, strings):
self._draw_label(e, string) self._draw_label(e, string)
self.hint_strings_updated.emit(strings)
self.set_mode.emit("hint")
def stop(self): def stop(self):
"""Stop hinting.""" """Stop hinting."""
@ -203,3 +244,12 @@ class HintManager:
e.removeFromDocument() e.removeFromDocument()
self._elems = None self._elems = None
self._labels = [] self._labels = []
self.set_mode.emit("normal")
def handle_partial_key(self, keystr):
"""Handle a new partial keypress."""
raise NotImplementedError
def fire(self, keystr):
"""Fire a completed hint."""
raise NotImplementedError

View File

@ -37,9 +37,11 @@ class CommandKeyParser(KeyParser):
"""Keyparser for command bindings. """Keyparser for command bindings.
Class attributes:
supports_count: If the keyparser should support counts.
Attributes: Attributes:
commandparser: Commandparser instance. commandparser: Commandparser instance.
supports_count: If the keyparser should support counts.
Signals: Signals:
set_cmd_text: Emitted when the statusbar should set a partial command. set_cmd_text: Emitted when the statusbar should set a partial command.

View File

@ -67,6 +67,10 @@ class TabbedBrowser(TabWidget):
cur_scroll_perc_changed: Scroll percentage of current tab changed. cur_scroll_perc_changed: Scroll percentage of current tab changed.
arg 1: x-position in %. arg 1: x-position in %.
arg 2: y-position in %. arg 2: y-position in %.
hint_strings_updated: Hint strings were updated.
arg: A list of hint strings.
set_mode: The input mode should be changed.
arg: The new mode as a string.
keypress: A key was pressed. keypress: A key was pressed.
arg: The QKeyEvent leading to the keypress. arg: The QKeyEvent leading to the keypress.
shutdown_complete: The shuttdown is completed. shutdown_complete: The shuttdown is completed.
@ -84,7 +88,9 @@ class TabbedBrowser(TabWidget):
cur_url_changed = pyqtSignal('QUrl') cur_url_changed = pyqtSignal('QUrl')
cur_link_hovered = pyqtSignal(str, str, str) cur_link_hovered = pyqtSignal(str, str, str)
cur_scroll_perc_changed = pyqtSignal(int, int) cur_scroll_perc_changed = pyqtSignal(int, int)
hint_strings_updated = pyqtSignal(list)
set_cmd_text = pyqtSignal(str) set_cmd_text = pyqtSignal(str)
set_mode = pyqtSignal(str)
keypress = pyqtSignal('QKeyEvent') keypress = pyqtSignal('QKeyEvent')
shutdown_complete = pyqtSignal() shutdown_complete = pyqtSignal()
quit = pyqtSignal() quit = pyqtSignal()
@ -238,6 +244,8 @@ class TabbedBrowser(TabWidget):
tab.temp_message.connect(self._filter.create(self.cur_temp_message)) tab.temp_message.connect(self._filter.create(self.cur_temp_message))
tab.urlChanged.connect(self._filter.create(self.cur_url_changed)) tab.urlChanged.connect(self._filter.create(self.cur_url_changed))
tab.titleChanged.connect(self._titleChanged_handler) tab.titleChanged.connect(self._titleChanged_handler)
tab.hintmanager.hint_strings_updated.connect(self.hint_strings_updated)
tab.hintmanager.set_mode.connect(self.set_mode)
# FIXME sometimes this doesn't load # FIXME sometimes this doesn't load
tab.show() tab.show()
tab.open_tab.connect(self.tabopen) tab.open_tab.connect(self.tabopen)