Use object registry for hintmanager.

This commit is contained in:
Florian Bruhin 2014-09-25 07:44:11 +02:00
parent e8ce45c440
commit 78949a8c1b
6 changed files with 72 additions and 133 deletions

View File

@ -404,13 +404,6 @@ class Application(QApplication):
status.keystring.setText) status.keystring.setText)
tabs.got_cmd.connect(self._commandrunner.run_safely) tabs.got_cmd.connect(self._commandrunner.run_safely)
# hints
kp[utypes.KeyMode.hint].fire_hint.connect(tabs.fire_hint)
kp[utypes.KeyMode.hint].filter_hints.connect(tabs.filter_hints)
kp[utypes.KeyMode.hint].keystring_updated.connect(tabs.handle_hint_key)
tabs.hint_strings_updated.connect(
kp[utypes.KeyMode.hint].on_hint_strings_updated)
# messages # messages
message_bridge.s_error.connect(status.disp_error) message_bridge.s_error.connect(status.disp_error)
message_bridge.s_info.connect(status.disp_temp_text) message_bridge.s_info.connect(status.disp_temp_text)

View File

@ -302,55 +302,6 @@ class CommandDispatcher:
for _ in range(count): for _ in range(count):
self._current_widget().go_forward() self._current_widget().go_forward()
@cmdutils.register(instance='command-dispatcher')
def hint(self, group=webelem.Group.all, target=hints.Target.normal,
*args: {'nargs': '*'}):
"""Start hinting.
Args:
group: The hinting mode to use.
- `all`: All clickable elements.
- `links`: Only links.
- `images`: Only images.
target: What to do with the selected element.
- `normal`: Open the link in the current tab.
- `tab`: Open the link in a new tab.
- `tab-bg`: Open the link in a new background tab.
- `yank`: Yank the link to the clipboard.
- `yank-primary`: Yank the link to the primary selection.
- `fill`: Fill the commandline with the command given as
argument.
- `rapid`: Open the link in a new tab and stay in hinting mode.
- `download`: Download the link.
- `userscript`: Call an userscript with `$QUTE_URL` set to the
link.
- `spawn`: Spawn a command.
*args: Arguments for spawn/userscript/fill.
- With `spawn`: The executable and arguments to spawn.
`{hint-url}` will get replaced by the selected
URL.
- With `userscript`: The userscript to execute.
- With `fill`: The command to fill the statusbar with.
`{hint-url}` will get replaced by the selected
URL.
"""
widget = self._current_widget()
frame = widget.page().mainFrame()
if frame is None:
raise cmdexc.CommandError("No frame focused!")
widget.hintmanager.start(frame, self._current_url(), group, target,
*args)
@cmdutils.register(instance='command-dispatcher', hide=True)
def follow_hint(self):
"""Follow the currently selected hint."""
self._current_widget().hintmanager.follow_hint()
def _navigate_incdec(self, url, tab, incdec): def _navigate_incdec(self, url, tab, incdec):
"""Helper method for :navigate when `where' is increment/decrement. """Helper method for :navigate when `where' is increment/decrement.
@ -424,12 +375,11 @@ class CommandDispatcher:
url = self._current_url() url = self._current_url()
if frame is None: if frame is None:
raise cmdexc.CommandError("No frame focused!") raise cmdexc.CommandError("No frame focused!")
hintmanager = objreg.get('hintmanager', scope='tab')
if where == 'prev': if where == 'prev':
widget.hintmanager.follow_prevnext(frame, url, prev=True, hintmanager.follow_prevnext(frame, url, prev=True, newtab=tab)
newtab=tab)
elif where == 'next': elif where == 'next':
widget.hintmanager.follow_prevnext(frame, url, prev=False, hintmanager.follow_prevnext(frame, url, prev=False, newtab=tab)
newtab=tab)
elif where == 'up': elif where == 'up':
self._navigate_up(url, tab) self._navigate_up(url, tab)
elif where in ('decrement', 'increment'): elif where in ('decrement', 'increment'):

View File

@ -30,9 +30,8 @@ from PyQt5.QtWidgets import QApplication
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.keyinput import modeman from qutebrowser.keyinput import modeman
from qutebrowser.browser import webelem from qutebrowser.browser import webelem
from qutebrowser.commands import userscripts, cmdexc from qutebrowser.commands import userscripts, cmdexc, cmdutils
from qutebrowser.utils import (usertypes, log, qtutils, message, objreg, from qutebrowser.utils import usertypes, log, qtutils, message, objreg
cmdutils)
ElemTuple = collections.namedtuple('ElemTuple', 'elem, label') ElemTuple = collections.namedtuple('ElemTuple', 'elem, label')
@ -96,13 +95,8 @@ class HintManager(QObject):
_context: The HintContext for the current invocation. _context: The HintContext for the current invocation.
Signals: Signals:
hint_strings_updated: Emitted when the possible hint strings changed.
arg: A list of hint strings.
mouse_event: Mouse event to be posted in the web view. mouse_event: Mouse event to be posted in the web view.
arg: A QMouseEvent arg: A QMouseEvent
openurl: Open a new URL
arg 0: URL to open as QUrl.
arg 1: True if it should be opened in a new tab, else False.
set_open_target: Set a new target to open the links in. set_open_target: Set a new target to open the links in.
""" """
@ -133,9 +127,7 @@ class HintManager(QObject):
Target.spawn: "Spawn command via hint...", Target.spawn: "Spawn command via hint...",
} }
hint_strings_updated = pyqtSignal(list)
mouse_event = pyqtSignal('QMouseEvent') mouse_event = pyqtSignal('QMouseEvent')
openurl = pyqtSignal('QUrl', bool)
set_open_target = pyqtSignal(str) set_open_target = pyqtSignal(str)
def __init__(self, parent=None): def __init__(self, parent=None):
@ -492,7 +484,8 @@ class HintManager(QObject):
for e, string in zip(elems, strings): for e, string in zip(elems, strings):
label = self._draw_label(e, string) label = self._draw_label(e, string)
self._context.elems[string] = ElemTuple(e, label) self._context.elems[string] = ElemTuple(e, label)
self.hint_strings_updated.emit(strings) keyparser = objreg.get('keyparsers')[usertypes.KeyMode.hint]
keyparser.update_bindings(strings)
def follow_prevnext(self, frame, baseurl, prev=False, newtab=False): def follow_prevnext(self, frame, baseurl, prev=False, newtab=False):
"""Click a "previous"/"next" element on the page. """Click a "previous"/"next" element on the page.
@ -511,31 +504,60 @@ class HintManager(QObject):
if url is None: if url is None:
raise cmdexc.CommandError("No {} links found!".format( raise cmdexc.CommandError("No {} links found!".format(
"prev" if prev else "forward")) "prev" if prev else "forward"))
self.openurl.emit(url, newtab) qtutils.ensure_valid(url)
if newtab:
objreg.get('tabbed-browser').tabopen(url, background=False)
else:
objreg.get('webview', scope='tab').openurl(url)
def start(self, mainframe, baseurl, group=webelem.Group.all, @cmdutils.register(instance='hintmanager', scope='tab', name='hint')
target=Target.normal, *args): def start(self, group=webelem.Group.all, target=Target.normal,
*args: {'nargs': '*'}):
"""Start hinting. """Start hinting.
Args: Args:
mainframe: The main QWebFrame. group: The hinting mode to use.
baseurl: URL of the current page.
group: Which group of elements to hint.
target: What to do with the link. See attribute docstring.
*args: Arguments for userscript/download
Emit: - `all`: All clickable elements.
hint_strings_updated: Emitted to update keypraser. - `links`: Only links.
- `images`: Only images.
target: What to do with the selected element.
- `normal`: Open the link in the current tab.
- `tab`: Open the link in a new tab.
- `tab-bg`: Open the link in a new background tab.
- `yank`: Yank the link to the clipboard.
- `yank-primary`: Yank the link to the primary selection.
- `fill`: Fill the commandline with the command given as
argument.
- `rapid`: Open the link in a new tab and stay in hinting mode.
- `download`: Download the link.
- `userscript`: Call an userscript with `$QUTE_URL` set to the
link.
- `spawn`: Spawn a command.
*args: Arguments for spawn/userscript/fill.
- With `spawn`: The executable and arguments to spawn.
`{hint-url}` will get replaced by the selected
URL.
- With `userscript`: The userscript to execute.
- With `fill`: The command to fill the statusbar with.
`{hint-url}` will get replaced by the selected
URL.
""" """
self._check_args(target, *args) tabbed_browser = objreg.get('tabbed-browser')
widget = tabbed_browser.currentWidget()
if widget is None:
raise cmdexc.CommandError("No WebView available yet!")
mainframe = widget.page().mainFrame()
if mainframe is None: if mainframe is None:
# This should never happen since we check frame before calling raise cmdexc.CommandError("No frame focused!")
# start. But since we had a bug where frame is None in self._check_args(target, *args)
# on_mode_left, we are extra careful here.
raise ValueError("start() was called with frame=None")
self._context = HintContext() self._context = HintContext()
self._context.target = target self._context.target = target
self._context.baseurl = baseurl self._context.baseurl = tabbed_browser.current_url()
self._context.frames = webelem.get_child_frames(mainframe) self._context.frames = webelem.get_child_frames(mainframe)
self._context.args = args self._context.args = args
self._init_elements(mainframe, group) self._init_elements(mainframe, group)
@ -634,6 +656,7 @@ class HintManager(QObject):
if self._context.target != Target.rapid: if self._context.target != Target.rapid:
modeman.maybe_leave(usertypes.KeyMode.hint, 'followed') modeman.maybe_leave(usertypes.KeyMode.hint, 'followed')
@cmdutils.register(instance='hintmanager', scope='tab', hide=True)
def follow_hint(self): def follow_hint(self):
"""Follow the currently selected hint.""" """Follow the currently selected hint."""
if not self._context.to_follow: if not self._context.to_follow:

View File

@ -23,12 +23,12 @@ Module attributes:
STARTCHARS: Possible chars for starting a commandline input. STARTCHARS: Possible chars for starting a commandline input.
""" """
from PyQt5.QtCore import pyqtSignal, Qt from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt
from qutebrowser.utils import message from qutebrowser.utils import message
from qutebrowser.config import config from qutebrowser.config import config
from qutebrowser.keyinput import keyparser from qutebrowser.keyinput import keyparser
from qutebrowser.utils import usertypes, log from qutebrowser.utils import usertypes, log, objreg
STARTCHARS = ":/?" STARTCHARS = ":/?"
@ -80,25 +80,17 @@ class HintKeyParser(keyparser.CommandKeyParser):
"""KeyChainParser for hints. """KeyChainParser for hints.
Signals:
fire_hint: When a hint keybinding was completed.
Arg: the keystring/hint string pressed.
filter_hints: When the filter text changed.
Arg: the text to filter hints with.
Attributes: Attributes:
_filtertext: The text to filter with. _filtertext: The text to filter with.
_last_press: The nature of the last keypress, a LastPress member. _last_press: The nature of the last keypress, a LastPress member.
""" """
fire_hint = pyqtSignal(str)
filter_hints = pyqtSignal(str)
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent, supports_count=False, supports_chains=True) super().__init__(parent, supports_count=False, supports_chains=True)
self._filtertext = '' self._filtertext = ''
self._last_press = LastPress.none self._last_press = LastPress.none
self.read_config('hint') self.read_config('hint')
self.keystring_updated.connect(self.on_keystring_updated)
def _handle_special_key(self, e): def _handle_special_key(self, e):
"""Override _handle_special_key to handle string filtering. """Override _handle_special_key to handle string filtering.
@ -112,11 +104,11 @@ class HintKeyParser(keyparser.CommandKeyParser):
True if event has been handled, False otherwise. True if event has been handled, False otherwise.
Emit: Emit:
filter_hints: Emitted when filter string has changed.
keystring_updated: Emitted when keystring has been changed. keystring_updated: Emitted when keystring has been changed.
""" """
log.keyboard.debug("Got special key 0x{:x} text {}".format( log.keyboard.debug("Got special key 0x{:x} text {}".format(
e.key(), e.text())) e.key(), e.text()))
hintmanager = objreg.get('hintmanager', scope='tab')
if e.key() == Qt.Key_Backspace: if e.key() == Qt.Key_Backspace:
log.keyboard.debug("Got backspace, mode {}, filtertext '{}', " log.keyboard.debug("Got backspace, mode {}, filtertext '{}', "
"keystring '{}'".format(self._last_press, "keystring '{}'".format(self._last_press,
@ -124,7 +116,7 @@ class HintKeyParser(keyparser.CommandKeyParser):
self._keystring)) self._keystring))
if self._last_press == LastPress.filtertext and self._filtertext: if self._last_press == LastPress.filtertext and self._filtertext:
self._filtertext = self._filtertext[:-1] self._filtertext = self._filtertext[:-1]
self.filter_hints.emit(self._filtertext) hintmanager.filter_hints(self._filtertext)
return True return True
elif self._last_press == LastPress.keystring and self._keystring: elif self._last_press == LastPress.keystring and self._keystring:
self._keystring = self._keystring[:-1] self._keystring = self._keystring[:-1]
@ -138,7 +130,7 @@ class HintKeyParser(keyparser.CommandKeyParser):
return super()._handle_special_key(e) return super()._handle_special_key(e)
else: else:
self._filtertext += e.text() self._filtertext += e.text()
self.filter_hints.emit(self._filtertext) hintmanager.filter_hints(self._filtertext)
self._last_press = LastPress.filtertext self._last_press = LastPress.filtertext
return True return True
@ -167,24 +159,25 @@ class HintKeyParser(keyparser.CommandKeyParser):
return self._handle_special_key(e) return self._handle_special_key(e)
def execute(self, cmdstr, keytype, count=None): def execute(self, cmdstr, keytype, count=None):
"""Handle a completed keychain. """Handle a completed keychain."""
Emit:
fire_hint: Emitted if keytype is chain
"""
if not isinstance(keytype, self.Type): if not isinstance(keytype, self.Type):
raise TypeError("Type {} is no Type member!".format(keytype)) raise TypeError("Type {} is no Type member!".format(keytype))
if keytype == self.Type.chain: if keytype == self.Type.chain:
self.fire_hint.emit(cmdstr) objreg.get('hintmanager', scope='tab').fire(cmdstr)
else: else:
# execute as command # execute as command
super().execute(cmdstr, keytype, count) super().execute(cmdstr, keytype, count)
def on_hint_strings_updated(self, strings): def update_bindings(self, strings):
"""Handler for HintManager's hint_strings_updated. """Update bindings when the hint strings changed.
Args: Args:
strings: A list of hint strings. strings: A list of hint strings.
""" """
self.bindings = {s: s for s in strings} self.bindings = {s: s for s in strings}
self._filtertext = '' self._filtertext = ''
@pyqtSlot(str)
def on_keystring_updated(self, keystr):
"""Update hintmanager when the keystring was updated."""
objreg.get('hintmanager', scope='tab').handle_partial_key(keystr)

View File

@ -67,8 +67,6 @@ class TabbedBrowser(tabwidget.TabWidget):
arg 1: x-position in %. arg 1: x-position in %.
arg 2: y-position in %. arg 2: y-position in %.
cur_load_status_changed: Loading status of current tab changed. cur_load_status_changed: Loading status of current tab changed.
hint_strings_updated: Hint strings were updated.
arg: A list of hint strings.
quit: The last tab was closed, quit application. quit: The last tab was closed, quit application.
resized: Emitted when the browser window has resized, so the completion resized: Emitted when the browser window has resized, so the completion
widget can adjust its size to it. widget can adjust its size to it.
@ -89,7 +87,6 @@ class TabbedBrowser(tabwidget.TabWidget):
cur_scroll_perc_changed = pyqtSignal(int, int) cur_scroll_perc_changed = pyqtSignal(int, int)
cur_load_status_changed = pyqtSignal(str) cur_load_status_changed = pyqtSignal(str)
start_download = pyqtSignal('QNetworkReply*') start_download = pyqtSignal('QNetworkReply*')
hint_strings_updated = pyqtSignal(list)
quit = pyqtSignal() quit = pyqtSignal()
resized = pyqtSignal('QRect') resized = pyqtSignal('QRect')
got_cmd = pyqtSignal(str) got_cmd = pyqtSignal(str)
@ -151,9 +148,6 @@ class TabbedBrowser(tabwidget.TabWidget):
self._filter.create(self.cur_load_status_changed, tab)) self._filter.create(self.cur_load_status_changed, tab))
tab.url_text_changed.connect( tab.url_text_changed.connect(
functools.partial(self.on_url_text_changed, tab)) functools.partial(self.on_url_text_changed, tab))
# hintmanager
tab.hintmanager.hint_strings_updated.connect(self.hint_strings_updated)
tab.hintmanager.openurl.connect(self.openurl)
self.cur_load_started.connect(self.on_cur_load_started) self.cur_load_started.connect(self.on_cur_load_started)
# downloads # downloads
page.start_download.connect(self.start_download) page.start_download.connect(self.start_download)
@ -370,21 +364,6 @@ class TabbedBrowser(tabwidget.TabWidget):
# We first want QWebPage to refresh. # We first want QWebPage to refresh.
QTimer.singleShot(0, check_scroll_pos) QTimer.singleShot(0, check_scroll_pos)
@pyqtSlot(str)
def handle_hint_key(self, keystr):
"""Handle a new hint keypress."""
self.currentWidget().hintmanager.handle_partial_key(keystr)
@pyqtSlot(str)
def fire_hint(self, keystr):
"""Fire a completed hint."""
self.currentWidget().hintmanager.fire(keystr)
@pyqtSlot(str)
def filter_hints(self, filterstr):
"""Filter displayed hints."""
self.currentWidget().hintmanager.filter_hints(filterstr)
@pyqtSlot(str, str) @pyqtSlot(str, str)
def on_config_changed(self, section, option): def on_config_changed(self, section, option):
"""Update tab config when config was changed.""" """Update tab config when config was changed."""

View File

@ -94,9 +94,10 @@ class WebView(QWebView):
objreg.register('webview', self, registry=self.registry) objreg.register('webview', self, registry=self.registry)
page = webpage.BrowserPage(self) page = webpage.BrowserPage(self)
self.setPage(page) self.setPage(page)
self.hintmanager = hints.HintManager(self) hintmanager = hints.HintManager(self)
self.hintmanager.mouse_event.connect(self.on_mouse_event) hintmanager.mouse_event.connect(self.on_mouse_event)
self.hintmanager.set_open_target.connect(self.set_force_open_target) hintmanager.set_open_target.connect(self.set_force_open_target)
objreg.register('hintmanager', hintmanager, registry=self.registry)
page.linkHovered.connect(self.linkHovered) page.linkHovered.connect(self.linkHovered)
page.mainFrame().loadStarted.connect(self.on_load_started) page.mainFrame().loadStarted.connect(self.on_load_started)
page.change_title.connect(self.titleChanged) page.change_title.connect(self.titleChanged)