From e169e2165da7892c0b9731a94481aebbc867df26 Mon Sep 17 00:00:00 2001 From: bttner <35602050+bttner@users.noreply.github.com> Date: Thu, 15 Feb 2018 12:02:42 +0100 Subject: [PATCH] Refactor TabbedBrowser from inheritance to composition --- qutebrowser/app.py | 2 +- qutebrowser/browser/commands.py | 51 ++++---- qutebrowser/browser/hints.py | 2 +- qutebrowser/browser/signalfilter.py | 4 +- qutebrowser/completion/models/miscmodels.py | 6 +- qutebrowser/mainwindow/mainwindow.py | 12 +- .../mainwindow/statusbar/backforward.py | 2 +- qutebrowser/mainwindow/statusbar/bar.py | 2 +- qutebrowser/mainwindow/tabbedbrowser.py | 122 +++++++++--------- qutebrowser/mainwindow/tabwidget.py | 16 +-- qutebrowser/misc/sessions.py | 9 +- qutebrowser/misc/utilcmds.py | 2 +- qutebrowser/utils/objreg.py | 2 +- tests/helpers/stubs.py | 38 +++--- tests/unit/browser/test_signalfilter.py | 16 +-- tests/unit/completion/test_models.py | 18 +-- .../mainwindow/statusbar/test_backforward.py | 12 +- tests/unit/mainwindow/test_tabwidget.py | 4 +- 18 files changed, 165 insertions(+), 155 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index ec477ce8f..546c1e17a 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -339,7 +339,7 @@ def _open_startpage(win_id=None): for cur_win_id in list(window_ids): # Copying as the dict could change tabbed_browser = objreg.get('tabbed-browser', scope='window', window=cur_win_id) - if tabbed_browser.count() == 0: + if tabbed_browser.widget.count() == 0: log.init.debug("Opening start pages") for url in config.val.url.start_pages: tabbed_browser.tabopen(url) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 69cb3142f..1eef43148 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -74,16 +74,16 @@ class CommandDispatcher: def _count(self): """Convenience method to get the widget count.""" - return self._tabbed_browser.count() + return self._tabbed_browser.widget.count() def _set_current_index(self, idx): """Convenience method to set the current widget index.""" cmdutils.check_overflow(idx, 'int') - self._tabbed_browser.setCurrentIndex(idx) + self._tabbed_browser.widget.setCurrentIndex(idx) def _current_index(self): """Convenience method to get the current widget index.""" - return self._tabbed_browser.currentIndex() + return self._tabbed_browser.widget.currentIndex() def _current_url(self): """Convenience method to get the current url.""" @@ -102,7 +102,7 @@ class CommandDispatcher: def _current_widget(self): """Get the currently active widget from a command.""" - widget = self._tabbed_browser.currentWidget() + widget = self._tabbed_browser.widget.currentWidget() if widget is None: raise cmdexc.CommandError("No WebView available yet!") return widget @@ -148,10 +148,10 @@ class CommandDispatcher: None if no widget was found. """ if count is None: - return self._tabbed_browser.currentWidget() + return self._tabbed_browser.widget.currentWidget() elif 1 <= count <= self._count(): cmdutils.check_overflow(count + 1, 'int') - return self._tabbed_browser.widget(count - 1) + return self._tabbed_browser.widget.widget(count - 1) else: return None @@ -164,7 +164,7 @@ class CommandDispatcher: if not show_error: return raise cmdexc.CommandError("No last focused tab!") - idx = self._tabbed_browser.indexOf(tab) + idx = self._tabbed_browser.widget.indexOf(tab) if idx == -1: raise cmdexc.CommandError("Last focused tab vanished!") self._set_current_index(idx) @@ -213,7 +213,7 @@ class CommandDispatcher: what's configured in 'tabs.select_on_remove'. count: The tab index to close, or None """ - tabbar = self._tabbed_browser.tabBar() + tabbar = self._tabbed_browser.widget.tabBar() selection_override = self._get_selection_override(prev, next_, opposite) @@ -265,7 +265,7 @@ class CommandDispatcher: return to_pin = not tab.data.pinned - self._tabbed_browser.set_tab_pinned(tab, to_pin) + self._tabbed_browser.widget.set_tab_pinned(tab, to_pin) @cmdutils.register(instance='command-dispatcher', name='open', maxsplit=0, scope='window') @@ -484,7 +484,8 @@ class CommandDispatcher: """ cmdutils.check_exclusive((bg, window), 'bw') curtab = self._current_widget() - cur_title = self._tabbed_browser.page_title(self._current_index()) + cur_title = self._tabbed_browser.widget.page_title( + self._current_index()) try: history = curtab.history.serialize() except browsertab.WebTabError as e: @@ -500,18 +501,18 @@ class CommandDispatcher: newtab = new_tabbed_browser.tabopen(background=bg) new_tabbed_browser = objreg.get('tabbed-browser', scope='window', window=newtab.win_id) - idx = new_tabbed_browser.indexOf(newtab) + idx = new_tabbed_browser.widget.indexOf(newtab) - new_tabbed_browser.set_page_title(idx, cur_title) + new_tabbed_browser.widget.set_page_title(idx, cur_title) if config.val.tabs.favicons.show: - new_tabbed_browser.setTabIcon(idx, curtab.icon()) + new_tabbed_browser.widget.setTabIcon(idx, curtab.icon()) if config.val.tabs.tabs_are_windows: - new_tabbed_browser.window().setWindowIcon(curtab.icon()) + new_tabbed_browser.widget.window().setWindowIcon(curtab.icon()) newtab.data.keep_icon = True newtab.history.deserialize(history) newtab.zoom.set_factor(curtab.zoom.factor()) - new_tabbed_browser.set_tab_pinned(newtab, curtab.data.pinned) + new_tabbed_browser.widget.set_tab_pinned(newtab, curtab.data.pinned) return newtab @cmdutils.register(instance='command-dispatcher', scope='window') @@ -847,7 +848,7 @@ class CommandDispatcher: keep: Stay in visual mode after yanking the selection. """ if what == 'title': - s = self._tabbed_browser.page_title(self._current_index()) + s = self._tabbed_browser.widget.page_title(self._current_index()) elif what == 'domain': port = self._current_url().port() s = '{}://{}{}'.format(self._current_url().scheme(), @@ -959,7 +960,7 @@ class CommandDispatcher: force: Avoid confirmation for pinned tabs. """ cmdutils.check_exclusive((prev, next_), 'pn') - cur_idx = self._tabbed_browser.currentIndex() + cur_idx = self._tabbed_browser.widget.currentIndex() assert cur_idx != -1 def _to_close(i): @@ -1076,11 +1077,11 @@ class CommandDispatcher: tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) - if not 0 < idx <= tabbed_browser.count(): + if not 0 < idx <= tabbed_browser.widget.count(): raise cmdexc.CommandError( "There's no tab with index {}!".format(idx)) - return (tabbed_browser, tabbed_browser.widget(idx-1)) + return (tabbed_browser, tabbed_browser.widget.widget(idx-1)) @cmdutils.register(instance='command-dispatcher', scope='window', maxsplit=0) @@ -1108,10 +1109,10 @@ class CommandDispatcher: tabbed_browser, tab = self._resolve_buffer_index(index) - window = tabbed_browser.window() + window = tabbed_browser.widget.window() window.activateWindow() window.raise_() - tabbed_browser.setCurrentWidget(tab) + tabbed_browser.widget.setCurrentWidget(tab) @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('index', choices=['last']) @@ -1195,7 +1196,7 @@ class CommandDispatcher: cur_idx = self._current_index() cmdutils.check_overflow(cur_idx, 'int') cmdutils.check_overflow(new_idx, 'int') - self._tabbed_browser.tabBar().moveTab(cur_idx, new_idx) + self._tabbed_browser.widget.tabBar().moveTab(cur_idx, new_idx) @cmdutils.register(instance='command-dispatcher', scope='window', maxsplit=0, no_replace_variables=True) @@ -1279,10 +1280,10 @@ class CommandDispatcher: idx = self._current_index() if idx != -1: - env['QUTE_TITLE'] = self._tabbed_browser.page_title(idx) + env['QUTE_TITLE'] = self._tabbed_browser.widget.page_title(idx) # FIXME:qtwebengine: If tab is None, run_async will fail! - tab = self._tabbed_browser.currentWidget() + tab = self._tabbed_browser.widget.currentWidget() try: url = self._tabbed_browser.current_url() @@ -2220,5 +2221,5 @@ class CommandDispatcher: pass return - window = self._tabbed_browser.window() + window = self._tabbed_browser.widget.window() window.setWindowState(window.windowState() ^ Qt.WindowFullScreen) diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 0390d5d1f..e22beeb1a 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -682,7 +682,7 @@ class HintManager(QObject): """ tabbed_browser = objreg.get('tabbed-browser', scope='window', window=self._win_id) - tab = tabbed_browser.currentWidget() + tab = tabbed_browser.widget.currentWidget() if tab is None: raise cmdexc.CommandError("No WebView available yet!") diff --git a/qutebrowser/browser/signalfilter.py b/qutebrowser/browser/signalfilter.py index 663aa67e7..7cc46abdb 100644 --- a/qutebrowser/browser/signalfilter.py +++ b/qutebrowser/browser/signalfilter.py @@ -76,11 +76,11 @@ class SignalFilter(QObject): tabbed_browser = objreg.get('tabbed-browser', scope='window', window=self._win_id) try: - tabidx = tabbed_browser.indexOf(tab) + tabidx = tabbed_browser.widget.indexOf(tab) except RuntimeError: # The tab has been deleted already return - if tabidx == tabbed_browser.currentIndex(): + if tabidx == tabbed_browser.widget.currentIndex(): if log_signal: log.signals.debug("emitting: {} (tab {})".format( debug.dbg_signal(signal, args), tabidx)) diff --git a/qutebrowser/completion/models/miscmodels.py b/qutebrowser/completion/models/miscmodels.py index 049d89295..8606bbf75 100644 --- a/qutebrowser/completion/models/miscmodels.py +++ b/qutebrowser/completion/models/miscmodels.py @@ -117,11 +117,11 @@ def _buffer(skip_win_id=None): if tabbed_browser.shutting_down: continue tabs = [] - for idx in range(tabbed_browser.count()): - tab = tabbed_browser.widget(idx) + for idx in range(tabbed_browser.widget.count()): + tab = tabbed_browser.widget.widget(idx) tabs.append(("{}/{}".format(win_id, idx + 1), tab.url().toDisplayString(), - tabbed_browser.page_title(idx))) + tabbed_browser.widget.page_title(idx))) cat = listcategory.ListCategory("{}".format(win_id), tabs, delete_func=delete_buffer) model.add_category(cat) diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py index 05482a1d5..b15e98e22 100644 --- a/qutebrowser/mainwindow/mainwindow.py +++ b/qutebrowser/mainwindow/mainwindow.py @@ -327,7 +327,7 @@ class MainWindow(QWidget): self.tabbed_browser) objreg.register('command-dispatcher', dispatcher, scope='window', window=self.win_id) - self.tabbed_browser.destroyed.connect( + self.tabbed_browser.widget.destroyed.connect( functools.partial(objreg.delete, 'command-dispatcher', scope='window', window=self.win_id)) @@ -347,10 +347,10 @@ class MainWindow(QWidget): def _add_widgets(self): """Add or readd all widgets to the VBox.""" - self._vbox.removeWidget(self.tabbed_browser) + self._vbox.removeWidget(self.tabbed_browser.widget) self._vbox.removeWidget(self._downloadview) self._vbox.removeWidget(self.status) - widgets = [self.tabbed_browser] + widgets = [self.tabbed_browser.widget] downloads_position = config.val.downloads.position if downloads_position == 'top': @@ -469,7 +469,7 @@ class MainWindow(QWidget): self.tabbed_browser.cur_scroll_perc_changed.connect( status.percentage.set_perc) - self.tabbed_browser.tab_index_changed.connect( + self.tabbed_browser.widget.tab_index_changed.connect( status.tabindex.on_tab_index_changed) self.tabbed_browser.cur_url_changed.connect(status.url.set_url) @@ -517,7 +517,7 @@ class MainWindow(QWidget): super().resizeEvent(e) self._update_overlay_geometries() self._downloadview.updateGeometry() - self.tabbed_browser.tabBar().refresh() + self.tabbed_browser.widget.tabBar().refresh() def showEvent(self, e): """Extend showEvent to register us as the last-visible-main-window. @@ -546,7 +546,7 @@ class MainWindow(QWidget): if crashsignal.is_crashing: e.accept() return - tab_count = self.tabbed_browser.count() + tab_count = self.tabbed_browser.widget.count() download_model = objreg.get('download-model', scope='window', window=self.win_id) download_count = download_model.running_downloads() diff --git a/qutebrowser/mainwindow/statusbar/backforward.py b/qutebrowser/mainwindow/statusbar/backforward.py index 8ea60ee75..5e244cf8c 100644 --- a/qutebrowser/mainwindow/statusbar/backforward.py +++ b/qutebrowser/mainwindow/statusbar/backforward.py @@ -32,7 +32,7 @@ class Backforward(textbase.TextBase): def on_tab_cur_url_changed(self, tabs): """Called on URL changes.""" - tab = tabs.currentWidget() + tab = tabs.widget.currentWidget() if tab is None: # pragma: no cover self.setText('') self.hide() diff --git a/qutebrowser/mainwindow/statusbar/bar.py b/qutebrowser/mainwindow/statusbar/bar.py index 8057bfdb8..9efc26858 100644 --- a/qutebrowser/mainwindow/statusbar/bar.py +++ b/qutebrowser/mainwindow/statusbar/bar.py @@ -268,7 +268,7 @@ class StatusBar(QWidget): """Get the currently displayed tab.""" window = objreg.get('tabbed-browser', scope='window', window=self._win_id) - return window.currentWidget() + return window.widget.currentWidget() def set_mode_active(self, mode, val): """Setter for self.{insert,command,caret}_active. diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 8cee35524..8d1498acb 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -22,7 +22,7 @@ import functools import attr -from PyQt5.QtWidgets import QSizePolicy +from PyQt5.QtWidgets import QSizePolicy, QWidget from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl from PyQt5.QtGui import QIcon @@ -50,7 +50,7 @@ class TabDeletedError(Exception): """Exception raised when _tab_index is called for a deleted tab.""" -class TabbedBrowser(tabwidget.TabWidget): +class TabbedBrowser(QWidget): """A TabWidget with QWebViews inside. @@ -110,17 +110,18 @@ class TabbedBrowser(tabwidget.TabWidget): new_tab = pyqtSignal(browsertab.AbstractTab, int) def __init__(self, *, win_id, private, parent=None): - super().__init__(win_id, parent) + super().__init__(parent) + self.widget = tabwidget.TabWidget(win_id, parent) self._win_id = win_id self._tab_insert_idx_left = 0 self._tab_insert_idx_right = -1 self.shutting_down = False - self.tabCloseRequested.connect(self.on_tab_close_requested) - self.new_tab_requested.connect(self.tabopen) - self.currentChanged.connect(self.on_current_changed) + self.widget.tabCloseRequested.connect(self.on_tab_close_requested) + self.widget.new_tab_requested.connect(self.tabopen) + self.widget.currentChanged.connect(self.on_current_changed) self.cur_load_started.connect(self.on_cur_load_started) - self.cur_fullscreen_requested.connect(self.tabBar().maybe_hide) - self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + self.cur_fullscreen_requested.connect(self.widget.tabBar().maybe_hide) + self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._undo_stack = [] self._filter = signalfilter.SignalFilter(win_id, self) self._now_focused = None @@ -128,12 +129,12 @@ class TabbedBrowser(tabwidget.TabWidget): self.search_options = {} self._local_marks = {} self._global_marks = {} - self.default_window_icon = self.window().windowIcon() + self.default_window_icon = self.widget.window().windowIcon() self.private = private config.instance.changed.connect(self._on_config_changed) def __repr__(self): - return utils.get_repr(self, count=self.count()) + return utils.get_repr(self, count=self.widget.count()) @pyqtSlot(str) def _on_config_changed(self, option): @@ -142,7 +143,7 @@ class TabbedBrowser(tabwidget.TabWidget): elif option == 'window.title_format': self._update_window_title() elif option in ['tabs.title.format', 'tabs.title.format_pinned']: - self._update_tab_titles() + self.widget.update_tab_titles() def _tab_index(self, tab): """Get the index of a given tab. @@ -150,7 +151,7 @@ class TabbedBrowser(tabwidget.TabWidget): Raises TabDeletedError if the tab doesn't exist anymore. """ try: - idx = self.indexOf(tab) + idx = self.widget.indexOf(tab) except RuntimeError as e: log.webview.debug("Got invalid tab ({})!".format(e)) raise TabDeletedError(e) @@ -166,8 +167,8 @@ class TabbedBrowser(tabwidget.TabWidget): iterating over the list. """ widgets = [] - for i in range(self.count()): - widget = self.widget(i) + for i in range(self.widget.count()): + widget = self.widget.widget(i) if widget is None: log.webview.debug("Got None-widget in tabbedbrowser!") else: @@ -186,12 +187,12 @@ class TabbedBrowser(tabwidget.TabWidget): if field is not None and ('{' + field + '}') not in title_format: return - idx = self.currentIndex() + idx = self.widget.currentIndex() if idx == -1: # (e.g. last tab removed) log.webview.debug("Not updating window title because index is -1") return - fields = self.get_tab_fields(idx) + fields = self.widget.get_tab_fields(idx) fields['id'] = self._win_id title = title_format.format(**fields) @@ -247,8 +248,8 @@ class TabbedBrowser(tabwidget.TabWidget): Return: The current URL as QUrl. """ - idx = self.currentIndex() - return super().tab_url(idx) + idx = self.widget.currentIndex() + return self.widget.tab_url(idx) def shutdown(self): """Try to shut down all tabs cleanly.""" @@ -284,7 +285,7 @@ class TabbedBrowser(tabwidget.TabWidget): new_undo: Whether the undo entry should be a new item in the stack. """ last_close = config.val.tabs.last_close - count = self.count() + count = self.widget.count() if last_close == 'ignore' and count == 1: return @@ -311,7 +312,7 @@ class TabbedBrowser(tabwidget.TabWidget): new_undo: Whether the undo entry should be a new item in the stack. crashed: Whether we're closing a tab with crashed renderer process. """ - idx = self.indexOf(tab) + idx = self.widget.indexOf(tab) if idx == -1: if crashed: return @@ -349,7 +350,7 @@ class TabbedBrowser(tabwidget.TabWidget): self._undo_stack[-1].append(entry) tab.shutdown() - self.removeTab(idx) + self.widget.removeTab(idx) if not crashed: # WORKAROUND for a segfault when we delete the crashed tab. # see https://bugreports.qt.io/browse/QTBUG-58698 @@ -362,14 +363,14 @@ class TabbedBrowser(tabwidget.TabWidget): last_close = config.val.tabs.last_close use_current_tab = False if last_close in ['blank', 'startpage', 'default-page']: - only_one_tab_open = self.count() == 1 - no_history = len(self.widget(0).history) == 1 + only_one_tab_open = self.widget.count() == 1 + no_history = len(self.widget.widget(0).history) == 1 urls = { 'blank': QUrl('about:blank'), 'startpage': config.val.url.start_pages[0], 'default-page': config.val.url.default_page, } - first_tab_url = self.widget(0).url() + first_tab_url = self.widget.widget(0).url() last_close_urlstr = urls[last_close].toString().rstrip('/') first_tab_urlstr = first_tab_url.toString().rstrip('/') last_close_url_used = first_tab_urlstr == last_close_urlstr @@ -379,14 +380,14 @@ class TabbedBrowser(tabwidget.TabWidget): for entry in reversed(self._undo_stack.pop()): if use_current_tab: self.openurl(entry.url, newtab=False) - newtab = self.widget(0) + newtab = self.widget.widget(0) use_current_tab = False else: newtab = self.tabopen(entry.url, background=False, idx=entry.index) newtab.history.deserialize(entry.history) - self.set_tab_pinned(newtab, entry.pinned) + self.widget.set_tab_pinned(newtab, entry.pinned) @pyqtSlot('QUrl', bool) def openurl(self, url, newtab): @@ -397,15 +398,15 @@ class TabbedBrowser(tabwidget.TabWidget): newtab: True to open URL in a new tab, False otherwise. """ qtutils.ensure_valid(url) - if newtab or self.currentWidget() is None: + if newtab or self.widget.currentWidget() is None: self.tabopen(url, background=False) else: - self.currentWidget().openurl(url) + self.widget.currentWidget().openurl(url) @pyqtSlot(int) def on_tab_close_requested(self, idx): """Close a tab via an index.""" - tab = self.widget(idx) + tab = self.widget.widget(idx) if tab is None: log.webview.debug("Got invalid tab {} for index {}!".format( tab, idx)) @@ -456,7 +457,7 @@ class TabbedBrowser(tabwidget.TabWidget): "related {}, idx {}".format( url, background, related, idx)) - if (config.val.tabs.tabs_are_windows and self.count() > 0 and + if (config.val.tabs.tabs_are_windows and self.widget.count() > 0 and not ignore_tabs_are_windows): window = mainwindow.MainWindow(private=self.private) window.show() @@ -466,12 +467,12 @@ class TabbedBrowser(tabwidget.TabWidget): related=related) tab = browsertab.create(win_id=self._win_id, private=self.private, - parent=self) + parent=self.widget) self._connect_tab_signals(tab) if idx is None: idx = self._get_new_tab_idx(related) - self.insertTab(idx, tab, "") + self.widget.insertTab(idx, tab, "") if url is not None: tab.openurl(url) @@ -482,10 +483,11 @@ class TabbedBrowser(tabwidget.TabWidget): # Make sure the background tab has the correct initial size. # With a foreground tab, it's going to be resized correctly by the # layout anyways. - tab.resize(self.currentWidget().size()) - self.tab_index_changed.emit(self.currentIndex(), self.count()) + tab.resize(self.widget.currentWidget().size()) + self.widget.tab_index_changed.emit(self.widget.currentIndex(), + self.widget.count()) else: - self.setCurrentWidget(tab) + self.widget.setCurrentWidget(tab) tab.show() self.new_tab.emit(tab, idx) @@ -530,11 +532,11 @@ class TabbedBrowser(tabwidget.TabWidget): """Update favicons when config was changed.""" for i, tab in enumerate(self.widgets()): if config.val.tabs.favicons.show: - self.setTabIcon(i, tab.icon()) + self.widget.setTabIcon(i, tab.icon()) if config.val.tabs.tabs_are_windows: self.window().setWindowIcon(tab.icon()) else: - self.setTabIcon(i, QIcon()) + self.widget.setTabIcon(i, QIcon()) if config.val.tabs.tabs_are_windows: self.window().setWindowIcon(self.default_window_icon) @@ -550,15 +552,15 @@ class TabbedBrowser(tabwidget.TabWidget): except TabDeletedError: # We can get signals for tabs we already deleted... return - self._update_tab_title(idx) + self.widget.update_tab_title(idx) if tab.data.keep_icon: tab.data.keep_icon = False else: - self.setTabIcon(idx, QIcon()) + self.widget.setTabIcon(idx, QIcon()) if (config.val.tabs.tabs_are_windows and config.val.tabs.favicons.show): self.window().setWindowIcon(self.default_window_icon) - if idx == self.currentIndex(): + if idx == self.widget.currentIndex(): self._update_window_title() @pyqtSlot() @@ -589,8 +591,8 @@ class TabbedBrowser(tabwidget.TabWidget): return log.webview.debug("Changing title for idx {} to '{}'".format( idx, text)) - self.set_page_title(idx, text) - if idx == self.currentIndex(): + self.widget.set_page_title(idx, text) + if idx == self.widget.currentIndex(): self._update_window_title() @pyqtSlot(browsertab.AbstractTab, QUrl) @@ -607,8 +609,8 @@ class TabbedBrowser(tabwidget.TabWidget): # We can get signals for tabs we already deleted... return - if not self.page_title(idx): - self.set_page_title(idx, url.toDisplayString()) + if not self.widget.page_title(idx): + self.widget.set_page_title(idx, url.toDisplayString()) @pyqtSlot(browsertab.AbstractTab, QIcon) def on_icon_changed(self, tab, icon): @@ -627,7 +629,7 @@ class TabbedBrowser(tabwidget.TabWidget): except TabDeletedError: # We can get signals for tabs we already deleted... return - self.setTabIcon(idx, icon) + self.widget.setTabIcon(idx, icon) if config.val.tabs.tabs_are_windows: self.window().setWindowIcon(icon) @@ -636,7 +638,7 @@ class TabbedBrowser(tabwidget.TabWidget): """Give focus to current tab if command mode was left.""" if mode in [usertypes.KeyMode.command, usertypes.KeyMode.prompt, usertypes.KeyMode.yesno]: - widget = self.currentWidget() + widget = self.widget.currentWidget() log.modes.debug("Left status-input mode, focusing {!r}".format( widget)) if widget is None: @@ -652,7 +654,7 @@ class TabbedBrowser(tabwidget.TabWidget): if idx == -1 or self.shutting_down: # closing the last tab (before quitting) or shutting down return - tab = self.widget(idx) + tab = self.widget.widget(idx) if tab is None: log.webview.debug("on_current_changed got called with invalid " "index {}".format(idx)) @@ -680,8 +682,8 @@ class TabbedBrowser(tabwidget.TabWidget): self._now_focused = tab self.current_tab_changed.emit(tab) QTimer.singleShot(0, self._update_window_title) - self._tab_insert_idx_left = self.currentIndex() - self._tab_insert_idx_right = self.currentIndex() + 1 + self._tab_insert_idx_left = self.widget.currentIndex() + self._tab_insert_idx_right = self.widget.currentIndex() + 1 @pyqtSlot() def on_cmd_return_pressed(self): @@ -699,9 +701,9 @@ class TabbedBrowser(tabwidget.TabWidget): stop = config.val.colors.tabs.indicator.stop system = config.val.colors.tabs.indicator.system color = utils.interpolate_color(start, stop, perc, system) - self.set_tab_indicator_color(idx, color) - self._update_tab_title(idx) - if idx == self.currentIndex(): + self.widget.set_tab_indicator_color(idx, color) + self.widget.update_tab_title(idx) + if idx == self.widget.currentIndex(): self._update_window_title() def on_load_finished(self, tab, ok): @@ -718,23 +720,23 @@ class TabbedBrowser(tabwidget.TabWidget): color = utils.interpolate_color(start, stop, 100, system) else: color = config.val.colors.tabs.indicator.error - self.set_tab_indicator_color(idx, color) - self._update_tab_title(idx) - if idx == self.currentIndex(): + self.widget.set_tab_indicator_color(idx, color) + self.widget.update_tab_title(idx) + if idx == self.widget.currentIndex(): self._update_window_title() tab.handle_auto_insert_mode(ok) @pyqtSlot() def on_scroll_pos_changed(self): """Update tab and window title when scroll position changed.""" - idx = self.currentIndex() + idx = self.widget.currentIndex() if idx == -1: # (e.g. last tab removed) log.webview.debug("Not updating scroll position because index is " "-1") return self._update_window_title('scroll_pos') - self._update_tab_title(idx, 'scroll_pos') + self.widget.update_tab_title(idx, 'scroll_pos') def _on_renderer_process_terminated(self, tab, status, code): """Show an error when a renderer process terminated.""" @@ -767,7 +769,7 @@ class TabbedBrowser(tabwidget.TabWidget): # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-58698 message.error(msg) self._remove_tab(tab, crashed=True) - if self.count() == 0: + if self.widget.count() == 0: self.tabopen(QUrl('about:blank')) def resizeEvent(self, e): @@ -804,7 +806,7 @@ class TabbedBrowser(tabwidget.TabWidget): if key != "'": message.error("Failed to set mark: url invalid") return - point = self.currentWidget().scroller.pos_px() + point = self.widget.currentWidget().scroller.pos_px() if key.isupper(): self._global_marks[key] = point, url @@ -825,7 +827,7 @@ class TabbedBrowser(tabwidget.TabWidget): except qtutils.QtValueError: urlkey = None - tab = self.currentWidget() + tab = self.widget.currentWidget() if key.isupper(): if key in self._global_marks: diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 965e5b219..abc6cedae 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -60,7 +60,7 @@ class TabWidget(QTabWidget): self.setTabBar(bar) bar.tabCloseRequested.connect(self.tabCloseRequested) bar.tabMoved.connect(functools.partial( - QTimer.singleShot, 0, self._update_tab_titles)) + QTimer.singleShot, 0, self.update_tab_titles)) bar.currentChanged.connect(self._on_current_changed) bar.new_tab_requested.connect(self._on_new_tab_requested) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) @@ -108,7 +108,7 @@ class TabWidget(QTabWidget): bar.set_tab_data(idx, 'pinned', pinned) tab.data.pinned = pinned - self._update_tab_title(idx) + self.update_tab_title(idx) def tab_indicator_color(self, idx): """Get the tab indicator color for the given index.""" @@ -117,13 +117,13 @@ class TabWidget(QTabWidget): def set_page_title(self, idx, title): """Set the tab title user data.""" self.tabBar().set_tab_data(idx, 'page-title', title) - self._update_tab_title(idx) + self.update_tab_title(idx) def page_title(self, idx): """Get the tab title user data.""" return self.tabBar().page_title(idx) - def _update_tab_title(self, idx, field=None): + def update_tab_title(self, idx, field=None): """Update the tab text for the given tab. Args: @@ -197,20 +197,20 @@ class TabWidget(QTabWidget): fields['scroll_pos'] = scroll_pos return fields - def _update_tab_titles(self): + def update_tab_titles(self): """Update all texts.""" for idx in range(self.count()): - self._update_tab_title(idx) + self.update_tab_title(idx) def tabInserted(self, idx): """Update titles when a tab was inserted.""" super().tabInserted(idx) - self._update_tab_titles() + self.update_tab_titles() def tabRemoved(self, idx): """Update titles when a tab was removed.""" super().tabRemoved(idx) - self._update_tab_titles() + self.update_tab_titles() def addTab(self, page, icon_or_text, text_or_empty=None): """Override addTab to use our own text setting logic. diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index a8a652dbb..dddf48b05 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -246,7 +246,7 @@ class SessionManager(QObject): if tabbed_browser.private: win_data['private'] = True for i, tab in enumerate(tabbed_browser.widgets()): - active = i == tabbed_browser.currentIndex() + active = i == tabbed_browser.widget.currentIndex() win_data['tabs'].append(self._save_tab(tab, active)) data['windows'].append(win_data) return data @@ -427,11 +427,12 @@ class SessionManager(QObject): if tab.get('active', False): tab_to_focus = i if new_tab.data.pinned: - tabbed_browser.set_tab_pinned(new_tab, new_tab.data.pinned) + tabbed_browser.widget.set_tab_pinned(new_tab, + new_tab.data.pinned) if tab_to_focus is not None: - tabbed_browser.setCurrentIndex(tab_to_focus) + tabbed_browser.widget.setCurrentIndex(tab_to_focus) if win.get('active', False): - QTimer.singleShot(0, tabbed_browser.activateWindow) + QTimer.singleShot(0, tabbed_browser.widget.activateWindow) if data['windows']: self.did_load = True diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py index d2743d56e..4b55eb04e 100644 --- a/qutebrowser/misc/utilcmds.py +++ b/qutebrowser/misc/utilcmds.py @@ -185,7 +185,7 @@ def debug_cache_stats(): tabbed_browser = objreg.get('tabbed-browser', scope='window', window='last-focused') # pylint: disable=protected-access - tab_bar = tabbed_browser.tabBar() + tab_bar = tabbed_browser.widget.tabBar() tabbed_browser_info = tab_bar._minimum_tab_size_hint_helper.cache_info() # pylint: enable=protected-access diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py index 8d44a9eb5..17fc34b92 100644 --- a/qutebrowser/utils/objreg.py +++ b/qutebrowser/utils/objreg.py @@ -171,7 +171,7 @@ def _get_tab_registry(win_id, tab_id): if tab_id == 'current': tabbed_browser = get('tabbed-browser', scope='window', window=win_id) - tab = tabbed_browser.currentWidget() + tab = tabbed_browser.widget.currentWidget() if tab is None: raise RegistryUnavailableError('window') tab_id = tab.tab_id diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index 64bc793cb..56c0a808e 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -497,37 +497,50 @@ class SessionManagerStub: def list_sessions(self): return self.sessions - class TabbedBrowserStub(QObject): """Stub for the tabbed-browser object.""" + def __init__(self, parent=None): + super().__init__(parent) + self.widget = TabWidgetStub() + self.shutting_down = False + self.opened_url = None + + def on_tab_close_requested(self, idx): + del self.widget.tabs[idx] + + def widgets(self): + return self.widget.tabs + + def tabopen(self, url): + self.opened_url = url + + def openurl(self, url, *, newtab): + self.opened_url = url + +class TabWidgetStub(QObject): + + """Stub for the tab-widget object.""" + new_tab = pyqtSignal(browsertab.AbstractTab, int) def __init__(self, parent=None): super().__init__(parent) self.tabs = [] - self.shutting_down = False self._qtabbar = QTabBar() self.index_of = None self.current_index = None - self.opened_url = None def count(self): return len(self.tabs) - def widgets(self): - return self.tabs - def widget(self, i): return self.tabs[i] def page_title(self, i): return self.tabs[i].title() - def on_tab_close_requested(self, idx): - del self.tabs[idx] - def tabBar(self): return self._qtabbar @@ -551,13 +564,6 @@ class TabbedBrowserStub(QObject): return None return self.tabs[idx - 1] - def tabopen(self, url): - self.opened_url = url - - def openurl(self, url, *, newtab): - self.opened_url = url - - class ApplicationStub(QObject): """Stub to insert as the app object in objreg.""" diff --git a/tests/unit/browser/test_signalfilter.py b/tests/unit/browser/test_signalfilter.py index 18f52a32a..957b85943 100644 --- a/tests/unit/browser/test_signalfilter.py +++ b/tests/unit/browser/test_signalfilter.py @@ -68,8 +68,8 @@ def objects(): @pytest.mark.parametrize('index_of, emitted', [(0, True), (1, False)]) def test_filtering(objects, tabbed_browser_stubs, index_of, emitted): browser = tabbed_browser_stubs[0] - browser.current_index = 0 - browser.index_of = index_of + browser.widget.current_index = 0 + browser.widget.index_of = index_of objects.signaller.signal.emit('foo') if emitted: assert objects.signaller.filtered_signal_arg == 'foo' @@ -80,8 +80,8 @@ def test_filtering(objects, tabbed_browser_stubs, index_of, emitted): @pytest.mark.parametrize('index_of, verb', [(0, 'emitting'), (1, 'ignoring')]) def test_logging(caplog, objects, tabbed_browser_stubs, index_of, verb): browser = tabbed_browser_stubs[0] - browser.current_index = 0 - browser.index_of = index_of + browser.widget.current_index = 0 + browser.widget.index_of = index_of with caplog.at_level(logging.DEBUG, logger='signals'): objects.signaller.signal.emit('foo') @@ -94,8 +94,8 @@ def test_logging(caplog, objects, tabbed_browser_stubs, index_of, verb): @pytest.mark.parametrize('index_of', [0, 1]) def test_no_logging(caplog, objects, tabbed_browser_stubs, index_of): browser = tabbed_browser_stubs[0] - browser.current_index = 0 - browser.index_of = index_of + browser.widget.current_index = 0 + browser.widget.index_of = index_of with caplog.at_level(logging.DEBUG, logger='signals'): objects.signaller.link_hovered.emit('foo') @@ -106,7 +106,7 @@ def test_no_logging(caplog, objects, tabbed_browser_stubs, index_of): def test_runtime_error(objects, tabbed_browser_stubs): """Test that there's no crash if indexOf() raises RuntimeError.""" browser = tabbed_browser_stubs[0] - browser.current_index = 0 - browser.index_of = RuntimeError + browser.widget.current_index = 0 + browser.widget.index_of = RuntimeError objects.signaller.signal.emit('foo') assert objects.signaller.filtered_signal_arg is None diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py index ff9a24112..b3865950c 100644 --- a/tests/unit/completion/test_models.py +++ b/tests/unit/completion/test_models.py @@ -528,12 +528,12 @@ def test_session_completion(qtmodeltester, session_manager_stub): def test_tab_completion(qtmodeltester, fake_web_tab, app_stub, win_registry, tabbed_browser_stubs): - tabbed_browser_stubs[0].tabs = [ + tabbed_browser_stubs[0].widget.tabs = [ fake_web_tab(QUrl('https://github.com'), 'GitHub', 0), fake_web_tab(QUrl('https://wikipedia.org'), 'Wikipedia', 1), fake_web_tab(QUrl('https://duckduckgo.com'), 'DuckDuckGo', 2), ] - tabbed_browser_stubs[1].tabs = [ + tabbed_browser_stubs[1].widget.tabs = [ fake_web_tab(QUrl('https://wiki.archlinux.org'), 'ArchWiki', 0), ] model = miscmodels.buffer() @@ -556,12 +556,12 @@ def test_tab_completion(qtmodeltester, fake_web_tab, app_stub, win_registry, def test_tab_completion_delete(qtmodeltester, fake_web_tab, app_stub, win_registry, tabbed_browser_stubs): """Verify closing a tab by deleting it from the completion widget.""" - tabbed_browser_stubs[0].tabs = [ + tabbed_browser_stubs[0].widget.tabs = [ fake_web_tab(QUrl('https://github.com'), 'GitHub', 0), fake_web_tab(QUrl('https://wikipedia.org'), 'Wikipedia', 1), fake_web_tab(QUrl('https://duckduckgo.com'), 'DuckDuckGo', 2) ] - tabbed_browser_stubs[1].tabs = [ + tabbed_browser_stubs[1].widget.tabs = [ fake_web_tab(QUrl('https://wiki.archlinux.org'), 'ArchWiki', 0), ] model = miscmodels.buffer() @@ -577,19 +577,19 @@ def test_tab_completion_delete(qtmodeltester, fake_web_tab, app_stub, assert model.data(idx) == '0/2' model.delete_cur_item(idx) - actual = [tab.url() for tab in tabbed_browser_stubs[0].tabs] + actual = [tab.url() for tab in tabbed_browser_stubs[0].widget.tabs] assert actual == [QUrl('https://github.com'), QUrl('https://duckduckgo.com')] def test_other_buffer_completion(qtmodeltester, fake_web_tab, app_stub, win_registry, tabbed_browser_stubs, info): - tabbed_browser_stubs[0].tabs = [ + tabbed_browser_stubs[0].widget.tabs = [ fake_web_tab(QUrl('https://github.com'), 'GitHub', 0), fake_web_tab(QUrl('https://wikipedia.org'), 'Wikipedia', 1), fake_web_tab(QUrl('https://duckduckgo.com'), 'DuckDuckGo', 2), ] - tabbed_browser_stubs[1].tabs = [ + tabbed_browser_stubs[1].widget.tabs = [ fake_web_tab(QUrl('https://wiki.archlinux.org'), 'ArchWiki', 0), ] info.win_id = 1 @@ -609,12 +609,12 @@ def test_other_buffer_completion(qtmodeltester, fake_web_tab, app_stub, def test_window_completion(qtmodeltester, fake_web_tab, tabbed_browser_stubs, info): - tabbed_browser_stubs[0].tabs = [ + tabbed_browser_stubs[0].widget.tabs = [ fake_web_tab(QUrl('https://github.com'), 'GitHub', 0), fake_web_tab(QUrl('https://wikipedia.org'), 'Wikipedia', 1), fake_web_tab(QUrl('https://duckduckgo.com'), 'DuckDuckGo', 2) ] - tabbed_browser_stubs[1].tabs = [ + tabbed_browser_stubs[1].widget.tabs = [ fake_web_tab(QUrl('https://wiki.archlinux.org'), 'ArchWiki', 0) ] diff --git a/tests/unit/mainwindow/statusbar/test_backforward.py b/tests/unit/mainwindow/statusbar/test_backforward.py index 6e594c0d2..11e3da616 100644 --- a/tests/unit/mainwindow/statusbar/test_backforward.py +++ b/tests/unit/mainwindow/statusbar/test_backforward.py @@ -43,8 +43,8 @@ def test_backforward_widget(backforward_widget, tabbed_browser_stubs, """Ensure the Backforward widget shows the correct text.""" tab = fake_web_tab(can_go_back=can_go_back, can_go_forward=can_go_forward) tabbed_browser = tabbed_browser_stubs[0] - tabbed_browser.current_index = 1 - tabbed_browser.tabs = [tab] + tabbed_browser.widget.current_index = 1 + tabbed_browser.widget.tabs = [tab] backforward_widget.enabled = True backforward_widget.on_tab_cur_url_changed(tabbed_browser) assert backforward_widget.text() == expected_text @@ -59,7 +59,7 @@ def test_backforward_widget(backforward_widget, tabbed_browser_stubs, # Check that the widget gets reset if empty. if can_go_back and can_go_forward: tab = fake_web_tab(can_go_back=False, can_go_forward=False) - tabbed_browser.tabs = [tab] + tabbed_browser.widget.tabs = [tab] backforward_widget.enabled = True backforward_widget.on_tab_cur_url_changed(tabbed_browser) assert backforward_widget.text() == '' @@ -70,15 +70,15 @@ def test_none_tab(backforward_widget, tabbed_browser_stubs, fake_web_tab): """Make sure nothing crashes when passing None as tab.""" tab = fake_web_tab(can_go_back=True, can_go_forward=True) tabbed_browser = tabbed_browser_stubs[0] - tabbed_browser.current_index = 1 - tabbed_browser.tabs = [tab] + tabbed_browser.widget.current_index = 1 + tabbed_browser.widget.tabs = [tab] backforward_widget.enabled = True backforward_widget.on_tab_cur_url_changed(tabbed_browser) assert backforward_widget.text() == '[<>]' assert backforward_widget.isVisible() - tabbed_browser.current_index = -1 + tabbed_browser.widget.current_index = -1 backforward_widget.on_tab_cur_url_changed(tabbed_browser) assert backforward_widget.text() == '' diff --git a/tests/unit/mainwindow/test_tabwidget.py b/tests/unit/mainwindow/test_tabwidget.py index 7ad22fcc3..36e6a0c48 100644 --- a/tests/unit/mainwindow/test_tabwidget.py +++ b/tests/unit/mainwindow/test_tabwidget.py @@ -71,7 +71,7 @@ class TestTabWidget: with qtbot.waitExposed(widget): widget.show() - benchmark(widget._update_tab_titles) + benchmark(widget.update_tab_titles) @pytest.mark.parametrize("num_tabs", [4, 10]) def test_add_remove_tab_benchmark(self, benchmark, browser, @@ -79,7 +79,7 @@ class TestTabWidget: """Benchmark for addTab and removeTab.""" def _run_bench(): for i in range(num_tabs): - browser.addTab(fake_web_tab(), 'foobar' + str(i)) + browser.widget.addTab(fake_web_tab(), 'foobar' + str(i)) with qtbot.waitExposed(browser): browser.show()