diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 19d5eec0e..a1ac4fb4f 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -404,13 +404,6 @@ class Application(QApplication): status.keystring.setText) 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 message_bridge.s_error.connect(status.disp_error) message_bridge.s_info.connect(status.disp_temp_text) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 27e1219a0..c692a5ec3 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -302,55 +302,6 @@ class CommandDispatcher: for _ in range(count): 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): """Helper method for :navigate when `where' is increment/decrement. @@ -424,12 +375,11 @@ class CommandDispatcher: url = self._current_url() if frame is None: raise cmdexc.CommandError("No frame focused!") + hintmanager = objreg.get('hintmanager', scope='tab') if where == 'prev': - widget.hintmanager.follow_prevnext(frame, url, prev=True, - newtab=tab) + hintmanager.follow_prevnext(frame, url, prev=True, newtab=tab) elif where == 'next': - widget.hintmanager.follow_prevnext(frame, url, prev=False, - newtab=tab) + hintmanager.follow_prevnext(frame, url, prev=False, newtab=tab) elif where == 'up': self._navigate_up(url, tab) elif where in ('decrement', 'increment'): diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 875d1eb4a..851e8302f 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -30,9 +30,8 @@ from PyQt5.QtWidgets import QApplication from qutebrowser.config import config from qutebrowser.keyinput import modeman from qutebrowser.browser import webelem -from qutebrowser.commands import userscripts, cmdexc -from qutebrowser.utils import (usertypes, log, qtutils, message, objreg, - cmdutils) +from qutebrowser.commands import userscripts, cmdexc, cmdutils +from qutebrowser.utils import usertypes, log, qtutils, message, objreg ElemTuple = collections.namedtuple('ElemTuple', 'elem, label') @@ -96,13 +95,8 @@ class HintManager(QObject): _context: The HintContext for the current invocation. 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. 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. """ @@ -133,9 +127,7 @@ class HintManager(QObject): Target.spawn: "Spawn command via hint...", } - hint_strings_updated = pyqtSignal(list) mouse_event = pyqtSignal('QMouseEvent') - openurl = pyqtSignal('QUrl', bool) set_open_target = pyqtSignal(str) def __init__(self, parent=None): @@ -492,7 +484,8 @@ class HintManager(QObject): for e, string in zip(elems, strings): label = self._draw_label(e, string) 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): """Click a "previous"/"next" element on the page. @@ -511,31 +504,60 @@ class HintManager(QObject): if url is None: raise cmdexc.CommandError("No {} links found!".format( "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, - target=Target.normal, *args): + @cmdutils.register(instance='hintmanager', scope='tab', name='hint') + def start(self, group=webelem.Group.all, target=Target.normal, + *args: {'nargs': '*'}): """Start hinting. Args: - mainframe: The main QWebFrame. - 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 + group: The hinting mode to use. - Emit: - hint_strings_updated: Emitted to update keypraser. + - `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. """ - 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: - # This should never happen since we check frame before calling - # start. But since we had a bug where frame is None in - # on_mode_left, we are extra careful here. - raise ValueError("start() was called with frame=None") + raise cmdexc.CommandError("No frame focused!") + self._check_args(target, *args) self._context = HintContext() 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.args = args self._init_elements(mainframe, group) @@ -634,6 +656,7 @@ class HintManager(QObject): if self._context.target != Target.rapid: modeman.maybe_leave(usertypes.KeyMode.hint, 'followed') + @cmdutils.register(instance='hintmanager', scope='tab', hide=True) def follow_hint(self): """Follow the currently selected hint.""" if not self._context.to_follow: diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index e22bcd801..7c0ea59bc 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -23,12 +23,12 @@ Module attributes: 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.config import config from qutebrowser.keyinput import keyparser -from qutebrowser.utils import usertypes, log +from qutebrowser.utils import usertypes, log, objreg STARTCHARS = ":/?" @@ -80,25 +80,17 @@ class HintKeyParser(keyparser.CommandKeyParser): """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: _filtertext: The text to filter with. _last_press: The nature of the last keypress, a LastPress member. """ - fire_hint = pyqtSignal(str) - filter_hints = pyqtSignal(str) - def __init__(self, parent=None): super().__init__(parent, supports_count=False, supports_chains=True) self._filtertext = '' self._last_press = LastPress.none self.read_config('hint') + self.keystring_updated.connect(self.on_keystring_updated) def _handle_special_key(self, e): """Override _handle_special_key to handle string filtering. @@ -112,11 +104,11 @@ class HintKeyParser(keyparser.CommandKeyParser): True if event has been handled, False otherwise. Emit: - filter_hints: Emitted when filter string has changed. keystring_updated: Emitted when keystring has been changed. """ log.keyboard.debug("Got special key 0x{:x} text {}".format( e.key(), e.text())) + hintmanager = objreg.get('hintmanager', scope='tab') if e.key() == Qt.Key_Backspace: log.keyboard.debug("Got backspace, mode {}, filtertext '{}', " "keystring '{}'".format(self._last_press, @@ -124,7 +116,7 @@ class HintKeyParser(keyparser.CommandKeyParser): self._keystring)) if self._last_press == LastPress.filtertext and self._filtertext: self._filtertext = self._filtertext[:-1] - self.filter_hints.emit(self._filtertext) + hintmanager.filter_hints(self._filtertext) return True elif self._last_press == LastPress.keystring and self._keystring: self._keystring = self._keystring[:-1] @@ -138,7 +130,7 @@ class HintKeyParser(keyparser.CommandKeyParser): return super()._handle_special_key(e) else: self._filtertext += e.text() - self.filter_hints.emit(self._filtertext) + hintmanager.filter_hints(self._filtertext) self._last_press = LastPress.filtertext return True @@ -167,24 +159,25 @@ class HintKeyParser(keyparser.CommandKeyParser): return self._handle_special_key(e) def execute(self, cmdstr, keytype, count=None): - """Handle a completed keychain. - - Emit: - fire_hint: Emitted if keytype is chain - """ + """Handle a completed keychain.""" if not isinstance(keytype, self.Type): raise TypeError("Type {} is no Type member!".format(keytype)) if keytype == self.Type.chain: - self.fire_hint.emit(cmdstr) + objreg.get('hintmanager', scope='tab').fire(cmdstr) else: # execute as command super().execute(cmdstr, keytype, count) - def on_hint_strings_updated(self, strings): - """Handler for HintManager's hint_strings_updated. + def update_bindings(self, strings): + """Update bindings when the hint strings changed. Args: strings: A list of hint strings. """ self.bindings = {s: s for s in strings} 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) diff --git a/qutebrowser/widgets/tabbedbrowser.py b/qutebrowser/widgets/tabbedbrowser.py index f07443197..208d04a48 100644 --- a/qutebrowser/widgets/tabbedbrowser.py +++ b/qutebrowser/widgets/tabbedbrowser.py @@ -67,8 +67,6 @@ class TabbedBrowser(tabwidget.TabWidget): arg 1: x-position in %. arg 2: y-position in %. 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. resized: Emitted when the browser window has resized, so the completion widget can adjust its size to it. @@ -89,7 +87,6 @@ class TabbedBrowser(tabwidget.TabWidget): cur_scroll_perc_changed = pyqtSignal(int, int) cur_load_status_changed = pyqtSignal(str) start_download = pyqtSignal('QNetworkReply*') - hint_strings_updated = pyqtSignal(list) quit = pyqtSignal() resized = pyqtSignal('QRect') got_cmd = pyqtSignal(str) @@ -151,9 +148,6 @@ class TabbedBrowser(tabwidget.TabWidget): self._filter.create(self.cur_load_status_changed, tab)) tab.url_text_changed.connect( 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) # downloads page.start_download.connect(self.start_download) @@ -370,21 +364,6 @@ class TabbedBrowser(tabwidget.TabWidget): # We first want QWebPage to refresh. 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) def on_config_changed(self, section, option): """Update tab config when config was changed.""" diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index 4dce57f63..e4ac1f1f3 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -94,9 +94,10 @@ class WebView(QWebView): objreg.register('webview', self, registry=self.registry) page = webpage.BrowserPage(self) self.setPage(page) - self.hintmanager = hints.HintManager(self) - self.hintmanager.mouse_event.connect(self.on_mouse_event) - self.hintmanager.set_open_target.connect(self.set_force_open_target) + hintmanager = hints.HintManager(self) + hintmanager.mouse_event.connect(self.on_mouse_event) + hintmanager.set_open_target.connect(self.set_force_open_target) + objreg.register('hintmanager', hintmanager, registry=self.registry) page.linkHovered.connect(self.linkHovered) page.mainFrame().loadStarted.connect(self.on_load_started) page.change_title.connect(self.titleChanged)