From 5b3597613a52d380704c857207a7ea024a72184e Mon Sep 17 00:00:00 2001 From: Jimmy Date: Sat, 7 Dec 2019 18:04:43 +1300 Subject: [PATCH 1/2] Scroll tab bar so current tab isn't at an edge It is unfortunate that everything we could override to fix the "gravity" of the visible portion of the tab bar is in QTabBarPrivate. Also that it has no public API for scrolling ... except for the buttons. Gets the QToolButtons that are children of the QTabWidget (actually of the QTabBarPrivate) and uses them to scroll the tab bar. Only works because they aren't hidden, they are just of zero size. Gets the tab indices at the edges of the tab bar and compares them to the current tab and if there is less than 5 tabs differnce it scrolls across to make up the difference. I didn't bother making the padding count configurable because I doubt this'll get merged. The throttle is mostly for resizing and shutting down (not sure why that is not a decorator). --- qutebrowser/mainwindow/tabwidget.py | 55 +++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 42c31c97e..8df3e51de 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -13,13 +13,14 @@ from qutebrowser.qt.core import (pyqtSignal, pyqtSlot, Qt, QSize, QRect, QPoint, QTimer, QUrl) from qutebrowser.qt.widgets import (QTabWidget, QTabBar, QSizePolicy, QProxyStyle, QStyle, QStylePainter, QStyleOptionTab, - QCommonStyle) + QCommonStyle, QToolButton) from qutebrowser.qt.gui import QIcon, QPalette, QColor from qutebrowser.utils import qtutils, objreg, utils, usertypes, log from qutebrowser.config import config, stylesheet -from qutebrowser.misc import objects, debugcachestats +from qutebrowser.misc import objects, debugcachestats, throttle from qutebrowser.browser import browsertab +from qutebrowser.qt import sip class TabWidget(QTabWidget): @@ -42,6 +43,7 @@ class TabWidget(QTabWidget): def __init__(self, win_id, parent=None): super().__init__(parent) + self.scroll_tab_bar = throttle.Throttle(self._scroll_tab_bar, 100) bar = TabBar(win_id, self) self.setStyle(TabBarStyle()) self.setTabBar(bar) @@ -56,6 +58,7 @@ class TabWidget(QTabWidget): bar.setDrawBase(False) self._init_config() config.instance.changed.connect(self._init_config) + self._scroll_left, self._scroll_right = self.findChildren(QToolButton) @config.change_filter('tabs') def _init_config(self): @@ -316,6 +319,49 @@ class TabWidget(QTabWidget): self.tab_bar().on_current_changed() self.update_tab_titles() self.tab_index_changed.emit(index, self.count()) + QTimer.singleShot(0, self.scroll_tab_bar) + + def _scroll_tab_bar(self): + """Scroll tab bar so that the current tab is not at an edge.""" + if sip.isdeleted(self): + # probably called async while shutting down + return + + num_tabs = 5 # padding required + idx = self.currentIndex() + if idx < 0: + return + + position = config.cache['tabs.position'] + if position == QTabWidget.TabPosition.North: + start = self.rect().topLeft() + end = self.rect().topRight() + elif position == QTabWidget.TabPosition.South: + start = self.rect().bottomLeft() + end = self.rect().bottomRight() + elif position == QTabWidget.TabPosition.West: + start = self.rect().topLeft() + end = self.rect().bottomLeft() + elif position == QTabWidget.TabPosition.East: + start = self.rect().topRight() + end = self.rect().bottomRight() + + start_idx = self.tabBar().tabAt(start) + end_idx = self.tabBar().tabAt(end) + if start_idx < 0 or end_idx < 0: + log.misc.warning("Could not get tabs at edges of tab bar.") + return + + count = num_tabs - (end_idx - idx) + if count > 0: + for _ in range(count): + self._scroll_right.click() + return + count = idx - start_idx + if count < num_tabs: + for _ in range(num_tabs - count): + self._scroll_left.click() + return @pyqtSlot() def _on_new_tab_requested(self): @@ -775,6 +821,11 @@ class TabBar(QTabBar): window=self._win_id) tabbed_browser.wheelEvent(e) + @pyqtSlot() + def tabLayoutChange(self): + if self.currentIndex() > -1: + QTimer.singleShot(0, self.parent().scroll_tab_bar) + @dataclasses.dataclass class Layouts: From 854196872bd10a85d7b93a34028f5d51bc898f33 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Sat, 7 Dec 2019 19:04:51 +1300 Subject: [PATCH 2/2] Allow scrolling the tab bar with shift+mousewheel --- qutebrowser/mainwindow/tabwidget.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 8df3e51de..332fc308d 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -321,6 +321,16 @@ class TabWidget(QTabWidget): self.tab_index_changed.emit(index, self.count()) QTimer.singleShot(0, self.scroll_tab_bar) + def scroll_tab_bar_left(self, count): + """Scroll the tab bar count tabs to the left.""" + for _ in range(count): + self._scroll_left.click() + + def scroll_tab_bar_right(self, count): + """Scroll the tab bar count tabs to the right.""" + for _ in range(count): + self._scroll_right.click() + def _scroll_tab_bar(self): """Scroll tab bar so that the current tab is not at an edge.""" if sip.isdeleted(self): @@ -354,13 +364,11 @@ class TabWidget(QTabWidget): count = num_tabs - (end_idx - idx) if count > 0: - for _ in range(count): - self._scroll_right.click() + self.scroll_tab_bar_right(count) return count = idx - start_idx if count < num_tabs: - for _ in range(num_tabs - count): - self._scroll_left.click() + self.scroll_tab_bar_left(num_tabs - count) return @pyqtSlot() @@ -801,7 +809,15 @@ class TabBar(QTabBar): e: The QWheelEvent """ if config.val.tabs.mousewheel_switching: - if utils.is_mac: + if e.modifiers() & Qt.KeyboardModifier.ShiftModifier: + delta = e.angleDelta() + if not delta: + return + if delta.y() < 0: + self.parent().scroll_tab_bar_left(1) + else: + self.parent().scroll_tab_bar_right(1) + elif utils.is_mac: # WORKAROUND for this not being customizable until Qt 6: # https://codereview.qt-project.org/c/qt/qtbase/+/327746 index = self.currentIndex()