From 08b562ea0c52e6dfa496055371ed668777adc418 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Sat, 14 Oct 2017 17:49:27 -0400 Subject: [PATCH 01/10] Add caching for tab sizes --- qutebrowser/mainwindow/tabwidget.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index c5566f877..55bd76f79 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -424,7 +424,7 @@ class TabBar(QTabBar): return super().mousePressEvent(e) - def minimumTabSizeHint(self, index, ellipsis: bool = True): + def minimumTabSizeHint(self, index, ellipsis: bool = True) -> QSize: """Set the minimum tab size to indicator/icon/... text. Args: @@ -434,11 +434,18 @@ class TabBar(QTabBar): Return: A QSize of the smallest tab size we can make. """ - text = '\u2026' if ellipsis else self.tabText(index) + return self.__minimumTabSizeHintHelper(self.tabText(index), + self.tabIcon(index), ellipsis) + + @functools.lru_cache(maxsize=100) + def __minimumTabSizeHintHelper(self, tab_text: str, + icon, + ellipsis: bool) -> QSize: + """Helper function to cache tab results.""" + text = '\u2026' if ellipsis else tab_text # Don't ever shorten if text is shorter than the ellipsis text_width = min(self.fontMetrics().width(text), - self.fontMetrics().width(self.tabText(index))) - icon = self.tabIcon(index) + self.fontMetrics().width(tab_text)) padding = config.val.tabs.padding indicator_padding = config.val.tabs.indicator_padding padding_h = padding.left + padding.right From e705ea7e568535528c8cf8200684214bf9364687 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Fri, 20 Oct 2017 15:40:11 -0400 Subject: [PATCH 02/10] Rename _minimum_tab_size_hint_helper --- qutebrowser/mainwindow/tabwidget.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 632486200..edabf0be0 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -447,13 +447,14 @@ class TabBar(QTabBar): Return: A QSize of the smallest tab size we can make. """ - return self.__minimumTabSizeHintHelper(self.tabText(index), - self.tabIcon(index), ellipsis) + return self._minimum_tab_size_hint_helper(self.tabText(index), + self.tabIcon(index), + ellipsis) @functools.lru_cache(maxsize=100) - def __minimumTabSizeHintHelper(self, tab_text: str, - icon, - ellipsis: bool) -> QSize: + def _minimum_tab_size_hint_helper(self, tab_text: str, + icon, + ellipsis: bool) -> QSize: """Helper function to cache tab results.""" text = '\u2026' if ellipsis else tab_text # Don't ever shorten if text is shorter than the ellipsis From fde4495bc795699aa413892fe30b5390b8592f99 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Fri, 20 Oct 2017 16:35:11 -0400 Subject: [PATCH 03/10] Clear cache on config changes --- qutebrowser/mainwindow/tabwidget.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index edabf0be0..10cdd4119 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -323,7 +323,7 @@ class TabBar(QTabBar): return self.parent().currentWidget() @pyqtSlot(str) - def _on_config_changed(self, option): + def _on_config_changed(self, option: str): if option == 'fonts.tabs': self._set_font() elif option == 'tabs.favicons.scale': @@ -338,6 +338,12 @@ class TabBar(QTabBar): if option.startswith('colors.tabs.'): self.update() + # Clear _minimum_tab_size_hint_helper cache when appropriate + if option in ["tabs.indicator_padding", + "tabs.padding", + "tabs.width.indicator"]: + self._minimum_tab_size_hint_helper.cache_clear() + def _on_show_switching_delay_changed(self): """Set timer interval when tabs.show_switching_delay got changed.""" self._auto_hide_timer.setInterval(config.val.tabs.show_switching_delay) @@ -455,7 +461,12 @@ class TabBar(QTabBar): def _minimum_tab_size_hint_helper(self, tab_text: str, icon, ellipsis: bool) -> QSize: - """Helper function to cache tab results.""" + """Helper function to cache tab results. + + Acessing config values in here should be added to _on_config_changed to + ensure cache is flushed when needed. + """ + print("running!") text = '\u2026' if ellipsis else tab_text # Don't ever shorten if text is shorter than the ellipsis text_width = min(self.fontMetrics().width(text), From caae1c70085d61413c760af0861a3f79e840179d Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Fri, 20 Oct 2017 21:50:35 -0400 Subject: [PATCH 04/10] Fix blowing cache for different icons --- qutebrowser/mainwindow/tabwidget.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 10cdd4119..b8b78beea 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -453,20 +453,25 @@ class TabBar(QTabBar): Return: A QSize of the smallest tab size we can make. """ + icon = self.tabIcon(index) + extent = self.style().pixelMetric(QStyle.PM_TabBarIconSize, None, self) + if not icon: + icon_width = 0 + else: + icon_width = icon.actualSize(QSize(extent, extent)).width() return self._minimum_tab_size_hint_helper(self.tabText(index), - self.tabIcon(index), + icon_width, ellipsis) - @functools.lru_cache(maxsize=100) + @functools.lru_cache(maxsize=2**10) def _minimum_tab_size_hint_helper(self, tab_text: str, - icon, + icon_width: int, ellipsis: bool) -> QSize: """Helper function to cache tab results. Acessing config values in here should be added to _on_config_changed to ensure cache is flushed when needed. """ - print("running!") text = '\u2026' if ellipsis else tab_text # Don't ever shorten if text is shorter than the ellipsis text_width = min(self.fontMetrics().width(text), @@ -476,14 +481,8 @@ class TabBar(QTabBar): padding_h = padding.left + padding.right padding_h += indicator_padding.left + indicator_padding.right padding_v = padding.top + padding.bottom - if icon.isNull(): - icon_size = QSize(0, 0) - else: - extent = self.style().pixelMetric(QStyle.PM_TabBarIconSize, None, - self) - icon_size = icon.actualSize(QSize(extent, extent)) height = self.fontMetrics().height() + padding_v - width = (text_width + icon_size.width() + + width = (text_width + icon_width + padding_h + config.val.tabs.width.indicator) return QSize(width, height) From b49947459918269189a8d633923dd9a9cd612567 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Sat, 21 Oct 2017 00:31:34 -0400 Subject: [PATCH 05/10] Prevent calling _tab_pinned on every tab twice --- qutebrowser/mainwindow/tabwidget.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index b8b78beea..4f2e33034 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -486,17 +486,15 @@ class TabBar(QTabBar): padding_h + config.val.tabs.width.indicator) return QSize(width, height) - def _tab_total_width_pinned(self): - """Get the current total width of pinned tabs. - - This width is calculated assuming no shortening due to ellipsis.""" - return sum(self.minimumTabSizeHint(idx, ellipsis=False).width() - for idx in range(self.count()) - if self._tab_pinned(idx)) - - def _pinnedCount(self) -> int: - """Get the number of pinned tabs.""" - return sum(self._tab_pinned(idx) for idx in range(self.count())) + def _pinned_statistics(self) -> (int, int): + """Get the number of pinned tabs and the total width of pinned tabs.""" + pinned_list = [idx + for idx in range(self.count()) + if self._tab_pinned(idx)] + pinned_count = len(pinned_list) + pinned_width = sum(self.minimumTabSizeHint(idx, ellipsis=False).width() + for idx in pinned_list) + return (pinned_count, pinned_width) def _tab_pinned(self, index: int) -> bool: """Return True if tab is pinned.""" @@ -535,8 +533,8 @@ class TabBar(QTabBar): return QSize() else: pinned = self._tab_pinned(index) - no_pinned_count = self.count() - self._pinnedCount() - pinned_width = self._tab_total_width_pinned() + pinned_count, pinned_width = self._pinned_statistics() + no_pinned_count = self.count() - pinned_count no_pinned_width = self.width() - pinned_width if pinned: From 49daa7aab88fca09034eae4dae36176587641125 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Sat, 21 Oct 2017 16:18:23 -0400 Subject: [PATCH 06/10] Add most recent tab bar to cache statistics --- qutebrowser/misc/utilcmds.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py index bf1e94586..e4c4fc033 100644 --- a/qutebrowser/misc/utilcmds.py +++ b/qutebrowser/misc/utilcmds.py @@ -181,9 +181,15 @@ def debug_cache_stats(): except ImportError: pass + tabbed_browser = objreg.get('tabbed-browser', scope='window', + window='last-focused') + tabbed_browser_info = tabbed_browser.tabBar(). \ + _minimum_tab_size_hint_helper.cache_info() + log.misc.debug('is_valid_prefix: {}'.format(prefix_info)) log.misc.debug('_render_stylesheet: {}'.format(render_stylesheet_info)) log.misc.debug('history: {}'.format(history_info)) + log.misc.debug('tab width cache: {}'.format(tabbed_browser_info)) @cmdutils.register(debug=True) From cb6f4313d7e74d1cf8ab7152958b617fad34c882 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Tue, 24 Oct 2017 10:18:10 -0400 Subject: [PATCH 07/10] Lower tabbar cache bound and clean up code --- qutebrowser/mainwindow/tabwidget.py | 4 ++-- qutebrowser/misc/utilcmds.py | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 4f2e33034..67d138fd4 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -463,13 +463,13 @@ class TabBar(QTabBar): icon_width, ellipsis) - @functools.lru_cache(maxsize=2**10) + @functools.lru_cache(maxsize=2**9) def _minimum_tab_size_hint_helper(self, tab_text: str, icon_width: int, ellipsis: bool) -> QSize: """Helper function to cache tab results. - Acessing config values in here should be added to _on_config_changed to + Config values accessed in here should be added to _on_config_changed to ensure cache is flushed when needed. """ text = '\u2026' if ellipsis else tab_text diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py index e4c4fc033..07949b771 100644 --- a/qutebrowser/misc/utilcmds.py +++ b/qutebrowser/misc/utilcmds.py @@ -171,6 +171,7 @@ def debug_cache_stats(): prefix_info = configdata.is_valid_prefix.cache_info() # pylint: disable=protected-access render_stylesheet_info = config._render_stylesheet.cache_info() + # pylint: enable=protected-access history_info = None try: @@ -183,8 +184,10 @@ def debug_cache_stats(): tabbed_browser = objreg.get('tabbed-browser', scope='window', window='last-focused') + # pylint: disable=protected-access tabbed_browser_info = tabbed_browser.tabBar(). \ _minimum_tab_size_hint_helper.cache_info() + # pylint: enable=protected-access log.misc.debug('is_valid_prefix: {}'.format(prefix_info)) log.misc.debug('_render_stylesheet: {}'.format(render_stylesheet_info)) From 97d719b1792e419aa53e36467d9e0186d71bbf75 Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Thu, 26 Oct 2017 17:56:28 -0400 Subject: [PATCH 08/10] Add a simple benchmark for _update_tab_titles --- tests/unit/mainwindow/test_tabwidget.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/unit/mainwindow/test_tabwidget.py b/tests/unit/mainwindow/test_tabwidget.py index e05e6a164..14b35bc03 100644 --- a/tests/unit/mainwindow/test_tabwidget.py +++ b/tests/unit/mainwindow/test_tabwidget.py @@ -52,3 +52,21 @@ class TestTabWidget: with qtbot.waitExposed(widget): widget.show() + + def test_update_tab_titles_benchmark(self, benchmark, widget, + qtbot, fake_web_tab): + """Benchmark for update_tab_titles.""" + widget.addTab(fake_web_tab(), 'foobar') + widget.addTab(fake_web_tab(), 'foobar2') + widget.addTab(fake_web_tab(), 'foobar3') + widget.addTab(fake_web_tab(), 'foobar4') + + with qtbot.waitExposed(widget): + widget.show() + + def bench(): + for _a in range(1000): + # pylint: disable=protected-access + widget._update_tab_titles() + # pylint: enable=protected-access + benchmark(bench) From 2a4163b2c7b3e8bb321ba92e01e73af2957288dc Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Fri, 27 Oct 2017 17:20:55 -0400 Subject: [PATCH 09/10] Fix ellipsis on pinned tabs with index > 10 See #3209 --- qutebrowser/mainwindow/tabwidget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index 67d138fd4..c3b7436d7 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -455,7 +455,7 @@ class TabBar(QTabBar): """ icon = self.tabIcon(index) extent = self.style().pixelMetric(QStyle.PM_TabBarIconSize, None, self) - if not icon: + if icon.isNull(): icon_width = 0 else: icon_width = icon.actualSize(QSize(extent, extent)).width() From 64b6852ae34929659f2376c4fd02545a012007bd Mon Sep 17 00:00:00 2001 From: Jay Kamat Date: Mon, 30 Oct 2017 12:12:57 -0400 Subject: [PATCH 10/10] Fix a couple style issues --- qutebrowser/mainwindow/tabwidget.py | 3 +-- qutebrowser/misc/utilcmds.py | 4 ++-- tests/unit/mainwindow/test_tabwidget.py | 7 +------ 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/qutebrowser/mainwindow/tabwidget.py b/qutebrowser/mainwindow/tabwidget.py index c3b7436d7..6384605fc 100644 --- a/qutebrowser/mainwindow/tabwidget.py +++ b/qutebrowser/mainwindow/tabwidget.py @@ -488,8 +488,7 @@ class TabBar(QTabBar): def _pinned_statistics(self) -> (int, int): """Get the number of pinned tabs and the total width of pinned tabs.""" - pinned_list = [idx - for idx in range(self.count()) + pinned_list = [idx for idx in range(self.count()) if self._tab_pinned(idx)] pinned_count = len(pinned_list) pinned_width = sum(self.minimumTabSizeHint(idx, ellipsis=False).width() diff --git a/qutebrowser/misc/utilcmds.py b/qutebrowser/misc/utilcmds.py index 07949b771..d52793669 100644 --- a/qutebrowser/misc/utilcmds.py +++ b/qutebrowser/misc/utilcmds.py @@ -185,8 +185,8 @@ def debug_cache_stats(): tabbed_browser = objreg.get('tabbed-browser', scope='window', window='last-focused') # pylint: disable=protected-access - tabbed_browser_info = tabbed_browser.tabBar(). \ - _minimum_tab_size_hint_helper.cache_info() + tab_bar = tabbed_browser.tabBar() + tabbed_browser_info = tab_bar._minimum_tab_size_hint_helper.cache_info() # pylint: enable=protected-access log.misc.debug('is_valid_prefix: {}'.format(prefix_info)) diff --git a/tests/unit/mainwindow/test_tabwidget.py b/tests/unit/mainwindow/test_tabwidget.py index 14b35bc03..dd87c7ce2 100644 --- a/tests/unit/mainwindow/test_tabwidget.py +++ b/tests/unit/mainwindow/test_tabwidget.py @@ -64,9 +64,4 @@ class TestTabWidget: with qtbot.waitExposed(widget): widget.show() - def bench(): - for _a in range(1000): - # pylint: disable=protected-access - widget._update_tab_titles() - # pylint: enable=protected-access - benchmark(bench) + benchmark(widget._update_tab_titles)