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.commands.keys import CommandKeyParser
from qutebrowser.commands.parsers import CommandParser, SearchParser
from qutebrowser.browser.hints import HintKeyParser
from qutebrowser.utils.appdirs import AppDirs
from qutebrowser.utils.misc import dotted_getattr
from qutebrowser.utils.debug import set_trace # pylint: disable=unused-import
@ -74,13 +75,14 @@ class QuteBrowser(QApplication):
Attributes:
mainwindow: The MainWindow QWidget.
commandparser: The main CommandParser instance.
keyparser: The main CommandKeyParser instance.
searchparser: The main SearchParser instance.
_keyparsers: A mapping from modes to keyparsers.
_dirs: AppDirs instance for config/cache directories.
_args: ArgumentParser instance.
_timers: List of used QTimers so they don't get GCed.
_shutting_down: True if we're currently shutting down.
_quit_status: The current quitting status.
_mode: The mode we're currently in.
"""
def __init__(self):
@ -88,6 +90,7 @@ class QuteBrowser(QApplication):
self._quit_status = {}
self._timers = []
self._shutting_down = False
self._mode = None
sys.excepthook = self._exception_hook
@ -108,40 +111,16 @@ class QuteBrowser(QApplication):
self.commandparser = CommandParser()
self.searchparser = SearchParser()
self.keyparser = CommandKeyParser(self)
self._keyparsers = {
"normal": CommandKeyParser(self),
"hint": HintKeyParser(self),
}
self._init_cmds()
self.mainwindow = MainWindow()
self.setQuitOnLastWindowClosed(False)
self.lastWindowClosed.connect(self.shutdown)
self.mainwindow.tabs.keypress.connect(
self.mainwindow.status.keypress)
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._connect_signals()
self.set_mode("normal")
self.mainwindow.show()
self._python_hacks()
@ -241,6 +220,45 @@ class QuteBrowser(QApplication):
timer.timeout.connect(lambda: None)
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):
"""Try to recover all open pages.
@ -340,6 +358,20 @@ class QuteBrowser(QApplication):
logging.debug("maybe_quit quitting.")
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)
def pyeval(self, s):
"""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)
@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)
def search(self, text, flags):
"""Search for text in the current page.

View File

@ -19,10 +19,41 @@
import math
from PyQt5.QtCore import pyqtSignal, QObject
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.
@ -34,6 +65,10 @@ class HintManager:
_frame: The QWebFrame to use.
_elems: The elements we're hinting currently.
_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 = {
@ -60,12 +95,16 @@ class HintManager:
top: {top}px;
"""
hint_strings_updated = pyqtSignal(list)
set_mode = pyqtSignal(str)
def __init__(self, frame):
"""Constructor.
Args:
frame: The QWebFrame to use for finding elements and drawing.
"""
super().__init__(frame)
self._frame = frame
self._elems = []
self._labels = []
@ -196,6 +235,8 @@ class HintManager:
strings = self._hint_strings(self._elems)
for e, string in zip(self._elems, strings):
self._draw_label(e, string)
self.hint_strings_updated.emit(strings)
self.set_mode.emit("hint")
def stop(self):
"""Stop hinting."""
@ -203,3 +244,12 @@ class HintManager:
e.removeFromDocument()
self._elems = None
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.
Class attributes:
supports_count: If the keyparser should support counts.
Attributes:
commandparser: Commandparser instance.
supports_count: If the keyparser should support counts.
Signals:
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.
arg 1: x-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.
arg: The QKeyEvent leading to the keypress.
shutdown_complete: The shuttdown is completed.
@ -84,7 +88,9 @@ class TabbedBrowser(TabWidget):
cur_url_changed = pyqtSignal('QUrl')
cur_link_hovered = pyqtSignal(str, str, str)
cur_scroll_perc_changed = pyqtSignal(int, int)
hint_strings_updated = pyqtSignal(list)
set_cmd_text = pyqtSignal(str)
set_mode = pyqtSignal(str)
keypress = pyqtSignal('QKeyEvent')
shutdown_complete = pyqtSignal()
quit = pyqtSignal()
@ -238,6 +244,8 @@ class TabbedBrowser(TabWidget):
tab.temp_message.connect(self._filter.create(self.cur_temp_message))
tab.urlChanged.connect(self._filter.create(self.cur_url_changed))
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
tab.show()
tab.open_tab.connect(self.tabopen)