From fa78054d37af061ec637af201d345881fc06eafc Mon Sep 17 00:00:00 2001 From: Skeptic Spriggan Date: Tue, 21 Oct 2025 10:13:15 +0200 Subject: [PATCH 01/11] feat: show zoom percentage in statusbar resolving #2870 --- doc/changelog.asciidoc | 1 + qutebrowser/browser/browsertab.py | 3 ++ qutebrowser/browser/webengine/webenginetab.py | 1 + qutebrowser/config/configdata.yml | 1 + qutebrowser/mainwindow/mainwindow.py | 2 + qutebrowser/mainwindow/statusbar/bar.py | 10 ++++- qutebrowser/mainwindow/statusbar/zoom.py | 24 +++++++++++ qutebrowser/mainwindow/tabbedbrowser.py | 4 ++ tests/helpers/stubs.py | 15 ++++++- tests/unit/mainwindow/statusbar/test_zoom.py | 40 +++++++++++++++++++ 10 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 qutebrowser/mainwindow/statusbar/zoom.py create mode 100644 tests/unit/mainwindow/statusbar/test_zoom.py diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index d926a81f4..c2ab25d9a 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -25,6 +25,7 @@ Added - The `:version` info now shows the X11 window manager / Wayland compositor name (mostly useful for bug/crash reports). - Support for hinting elements which are part of an (open) shadow DOM. +- Show zoom percentage in statusbar. (#2870) Changed ~~~~~~~ diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index 597b8d37c..0c72dd25f 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -434,6 +434,9 @@ class AbstractZoom(QObject): """Attribute ``zoom`` of AbstractTab for controlling zoom.""" + #: Signal emitted when a tab's zoom factor changed (float) + factor_changed = pyqtSignal(float) + def __init__(self, tab: 'AbstractTab', parent: QWidget = None) -> None: super().__init__(parent) self._tab = tab diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 726a2efab..90965d217 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -730,6 +730,7 @@ class WebEngineZoom(browsertab.AbstractZoom): def _set_factor_internal(self, factor): self._widget.setZoomFactor(factor) + self.factor_changed.emit(factor) class WebEngineElements(browsertab.AbstractElements): diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index 9a8c04ff7..83b6fc186 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -2195,6 +2195,7 @@ statusbar.widgets: format string via `clock:...`. For supported format strings, see https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes[the Python datetime documentation]." + - zoom: "Display zoom percentage (e.g. `200%`)" none_ok: true default: ['keypress', 'search_match', 'url', 'scroll', 'history', 'tabs', 'progress'] desc: "List of widgets displayed in the statusbar." diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py index dcbaf589d..9924e5110 100644 --- a/qutebrowser/mainwindow/mainwindow.py +++ b/qutebrowser/mainwindow/mainwindow.py @@ -525,6 +525,8 @@ class MainWindow(QWidget): self.tabbed_browser.cur_scroll_perc_changed.connect( self.status.percentage.set_perc) + self.tabbed_browser.cur_zoom_changed.connect( + self.status.zoom.on_zoom_changed) self.tabbed_browser.widget.tab_index_changed.connect( self.status.tabindex.on_tab_index_changed) diff --git a/qutebrowser/mainwindow/statusbar/bar.py b/qutebrowser/mainwindow/statusbar/bar.py index 4b72ad59f..f78643154 100644 --- a/qutebrowser/mainwindow/statusbar/bar.py +++ b/qutebrowser/mainwindow/statusbar/bar.py @@ -16,7 +16,8 @@ from qutebrowser.keyinput import modeman from qutebrowser.utils import usertypes, log, objreg, utils from qutebrowser.mainwindow.statusbar import (backforward, command, progress, keystring, percentage, url, - tabindex, textbase, clock, searchmatch) + tabindex, textbase, clock, searchmatch, + zoom) @dataclasses.dataclass @@ -190,6 +191,7 @@ class StatusBar(QWidget): self.keystring = keystring.KeyString() self.prog = progress.Progress(self) self.clock = clock.Clock() + self.zoom = zoom.Zoom() self._text_widgets = [] self._draw_widgets() @@ -223,6 +225,8 @@ class StatusBar(QWidget): return new_text_widget elif key.startswith('clock:') or key == 'clock': return self.clock + elif key == 'zoom': + return self.zoom else: raise utils.Unreachable(key) @@ -272,7 +276,8 @@ class StatusBar(QWidget): # Start with widgets hidden and show them when needed for widget in [self.url, self.percentage, self.backforward, self.tabindex, - self.keystring, self.prog, self.clock, *self._text_widgets]: + self.keystring, self.prog, self.clock, + self.zoom, *self._text_widgets]: assert isinstance(widget, QWidget) if widget in [self.prog, self.backforward]: widget.enabled = False # type: ignore[attr-defined] @@ -426,6 +431,7 @@ class StatusBar(QWidget): self.prog.on_tab_changed(tab) self.percentage.on_tab_changed(tab) self.backforward.on_tab_changed(tab) + self.zoom.on_tab_changed(tab) self.maybe_hide() assert tab.is_private == self._color_flags.private diff --git a/qutebrowser/mainwindow/statusbar/zoom.py b/qutebrowser/mainwindow/statusbar/zoom.py new file mode 100644 index 000000000..f8da05358 --- /dev/null +++ b/qutebrowser/mainwindow/statusbar/zoom.py @@ -0,0 +1,24 @@ +"""Zoom percentage displayed in the statusbar.""" + +from qutebrowser.mainwindow.statusbar import textbase + +from qutebrowser.qt.core import pyqtSlot + +class Zoom(textbase.TextBase): + + """Shows percentage indicator.""" + + def __init__(self, parent=None): + super().__init__(parent) + self.setText("100%") + + @pyqtSlot(float) + def on_zoom_changed(self, factor): + percentage = int(100 * factor) + self.setText(f"{percentage}%") + + def on_tab_changed(self, tab): + """Update zoom when tab changed.""" + percentage = int(100 * tab.zoom.factor()) + + self.setText(f"{percentage}%") diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index e0938ae36..9b33387cf 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -168,6 +168,7 @@ class TabbedBrowser(QWidget): cur_load_started: Current tab started loading (load_started) cur_load_finished: Current tab finished loading (load_finished) cur_url_changed: Current URL changed. + cur_zoom_changed: Zoom factor of current tab changed. cur_link_hovered: Link hovered in current tab (link_hovered) cur_scroll_perc_changed: Scroll percentage of current tab changed. arg 1: x-position in %. @@ -189,6 +190,7 @@ class TabbedBrowser(QWidget): cur_url_changed = pyqtSignal(QUrl) cur_link_hovered = pyqtSignal(str) cur_scroll_perc_changed = pyqtSignal(int, int) + cur_zoom_changed = pyqtSignal(float) cur_load_status_changed = pyqtSignal(usertypes.LoadStatus) cur_search_match_changed = pyqtSignal(browsertab.SearchMatch) cur_fullscreen_requested = pyqtSignal(bool) @@ -337,6 +339,8 @@ class TabbedBrowser(QWidget): self._filter.create(self.cur_load_started, tab)) tab.scroller.perc_changed.connect( self._filter.create(self.cur_scroll_perc_changed, tab)) + tab.zoom.factor_changed.connect( + self._filter.create(self.cur_zoom_changed, tab)) tab.url_changed.connect( self._filter.create(self.cur_url_changed, tab)) tab.load_status_changed.connect( diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index 50528ee8b..c7d1f8aca 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -204,6 +204,17 @@ class FakeWebTabScroller(browsertab.AbstractScroller): def pos_perc(self): return self._pos_perc +class FakeWebTabZoom(browsertab.AbstractZoom): + + """Fake AbstractZoom to use in tests.""" + + def __init__(self, tab, factor): + super().__init__(tab) + self._factor = factor + + def factor(self): + return self._factor + class FakeWebTabHistory(browsertab.AbstractHistory): @@ -245,7 +256,8 @@ class FakeWebTab(browsertab.AbstractTab): def __init__(self, url=QUrl(), title='', tab_id=0, *, scroll_pos_perc=(0, 0), load_status=usertypes.LoadStatus.success, - progress=0, can_go_back=None, can_go_forward=None): + progress=0, can_go_back=None, can_go_forward=None, + zoom_factor=1): super().__init__(win_id=0, mode_manager=None, private=False) self._load_status = load_status self._title = title @@ -254,6 +266,7 @@ class FakeWebTab(browsertab.AbstractTab): self.history = FakeWebTabHistory(self, can_go_back=can_go_back, can_go_forward=can_go_forward) self.scroller = FakeWebTabScroller(self, scroll_pos_perc) + self.zoom = FakeWebTabZoom(self, zoom_factor) self.audio = FakeWebTabAudio(self) self.private_api = FakeWebTabPrivate(tab=self, mode_manager=None) wrapped = QWidget() diff --git a/tests/unit/mainwindow/statusbar/test_zoom.py b/tests/unit/mainwindow/statusbar/test_zoom.py new file mode 100644 index 000000000..d4ae0780f --- /dev/null +++ b/tests/unit/mainwindow/statusbar/test_zoom.py @@ -0,0 +1,40 @@ +"""Test Zoom widget.""" + +import pytest + +from qutebrowser.mainwindow.statusbar.zoom import Zoom + + +@pytest.fixture +def zoom(qtbot): + """Fixture providing a Percentage widget.""" + widget = Zoom() + qtbot.add_widget(widget) + return widget + + +@pytest.mark.parametrize('factor, expected', [ + (1, '100%'), + (1.5, '150%'), + (2, '200%'), + (0.5, '50%'), + (0.25, '25%'), +]) +def test_percentage_texts(zoom, factor, expected): + """Test text displayed by the widget based on the zoom factor of a tab. + + Args: + factor: zoom factor of the tab as a float. + expected: expected text given factor. + """ + zoom.on_zoom_changed(factor=factor) + assert zoom.text() == expected + + +def test_tab_change(zoom, fake_web_tab): + """Test zoom factor change when switching tabs.""" + zoom.on_zoom_changed(factor=2) + assert zoom.text() == '200%' + tab = fake_web_tab(zoom_factor=0.5) + zoom.on_tab_changed(tab) + assert zoom.text() == '50%' From aaa19f795c8f2b7fe4dd87872fbc01bbf957538c Mon Sep 17 00:00:00 2001 From: Skeptic Spriggan Date: Tue, 21 Oct 2025 10:44:04 +0200 Subject: [PATCH 02/11] feat: show zoom percentage in statusbar resolving #2870 --- qutebrowser/mainwindow/statusbar/zoom.py | 4 +++- tests/helpers/stubs.py | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/qutebrowser/mainwindow/statusbar/zoom.py b/qutebrowser/mainwindow/statusbar/zoom.py index f8da05358..e059ca05b 100644 --- a/qutebrowser/mainwindow/statusbar/zoom.py +++ b/qutebrowser/mainwindow/statusbar/zoom.py @@ -4,6 +4,7 @@ from qutebrowser.mainwindow.statusbar import textbase from qutebrowser.qt.core import pyqtSlot + class Zoom(textbase.TextBase): """Shows percentage indicator.""" @@ -14,11 +15,12 @@ class Zoom(textbase.TextBase): @pyqtSlot(float) def on_zoom_changed(self, factor): + """Update percentage when factor changed.""" percentage = int(100 * factor) self.setText(f"{percentage}%") def on_tab_changed(self, tab): - """Update zoom when tab changed.""" + """Update percentage when tab changed.""" percentage = int(100 * tab.zoom.factor()) self.setText(f"{percentage}%") diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index c7d1f8aca..7a5011724 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -204,6 +204,7 @@ class FakeWebTabScroller(browsertab.AbstractScroller): def pos_perc(self): return self._pos_perc + class FakeWebTabZoom(browsertab.AbstractZoom): """Fake AbstractZoom to use in tests.""" From 421511d26defdf25b661b0f6cee02e382163a7e8 Mon Sep 17 00:00:00 2001 From: Skeptic Spriggan Date: Tue, 21 Oct 2025 11:46:09 +0200 Subject: [PATCH 03/11] feat: show zoom percentage in statusbar resolving #2870 --- qutebrowser/mainwindow/statusbar/zoom.py | 10 +++++----- tests/unit/mainwindow/statusbar/test_zoom.py | 8 +++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/qutebrowser/mainwindow/statusbar/zoom.py b/qutebrowser/mainwindow/statusbar/zoom.py index e059ca05b..fd0ed8129 100644 --- a/qutebrowser/mainwindow/statusbar/zoom.py +++ b/qutebrowser/mainwindow/statusbar/zoom.py @@ -1,25 +1,25 @@ """Zoom percentage displayed in the statusbar.""" +from qutebrowser.browser import browsertab from qutebrowser.mainwindow.statusbar import textbase - -from qutebrowser.qt.core import pyqtSlot +from qutebrowser.qt.core import pyqtSlot, QObject class Zoom(textbase.TextBase): """Shows percentage indicator.""" - def __init__(self, parent=None): + def __init__(self, parent: QObject = None) -> None: super().__init__(parent) self.setText("100%") @pyqtSlot(float) - def on_zoom_changed(self, factor): + def on_zoom_changed(self, factor: float) -> None: """Update percentage when factor changed.""" percentage = int(100 * factor) self.setText(f"{percentage}%") - def on_tab_changed(self, tab): + def on_tab_changed(self, tab: browsertab.AbstractTab) -> None: """Update percentage when tab changed.""" percentage = int(100 * tab.zoom.factor()) diff --git a/tests/unit/mainwindow/statusbar/test_zoom.py b/tests/unit/mainwindow/statusbar/test_zoom.py index d4ae0780f..48370b996 100644 --- a/tests/unit/mainwindow/statusbar/test_zoom.py +++ b/tests/unit/mainwindow/statusbar/test_zoom.py @@ -3,10 +3,12 @@ import pytest from qutebrowser.mainwindow.statusbar.zoom import Zoom +from typing import Any +import pytestqt.qtbot @pytest.fixture -def zoom(qtbot): +def zoom(qtbot: pytestqt.qtbot.QtBot) -> Zoom: """Fixture providing a Percentage widget.""" widget = Zoom() qtbot.add_widget(widget) @@ -20,7 +22,7 @@ def zoom(qtbot): (0.5, '50%'), (0.25, '25%'), ]) -def test_percentage_texts(zoom, factor, expected): +def test_percentage_texts(zoom: Zoom, factor: float, expected: str) -> None: """Test text displayed by the widget based on the zoom factor of a tab. Args: @@ -31,7 +33,7 @@ def test_percentage_texts(zoom, factor, expected): assert zoom.text() == expected -def test_tab_change(zoom, fake_web_tab): +def test_tab_change(zoom: Zoom, fake_web_tab: Any) -> None: """Test zoom factor change when switching tabs.""" zoom.on_zoom_changed(factor=2) assert zoom.text() == '200%' From e6d0de2a46643adf707adec2a14ec762bf65b108 Mon Sep 17 00:00:00 2001 From: Skeptic Spriggan Date: Tue, 21 Oct 2025 12:15:29 +0200 Subject: [PATCH 04/11] feat: show zoom percentage in statusbar resolving #2870 --- qutebrowser/mainwindow/statusbar/zoom.py | 2 +- tests/unit/mainwindow/statusbar/test_zoom.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/qutebrowser/mainwindow/statusbar/zoom.py b/qutebrowser/mainwindow/statusbar/zoom.py index fd0ed8129..af54a38f6 100644 --- a/qutebrowser/mainwindow/statusbar/zoom.py +++ b/qutebrowser/mainwindow/statusbar/zoom.py @@ -7,7 +7,7 @@ from qutebrowser.qt.core import pyqtSlot, QObject class Zoom(textbase.TextBase): - """Shows percentage indicator.""" + """Shows zoom percentage in current tab.""" def __init__(self, parent: QObject = None) -> None: super().__init__(parent) diff --git a/tests/unit/mainwindow/statusbar/test_zoom.py b/tests/unit/mainwindow/statusbar/test_zoom.py index 48370b996..dcc367af5 100644 --- a/tests/unit/mainwindow/statusbar/test_zoom.py +++ b/tests/unit/mainwindow/statusbar/test_zoom.py @@ -9,7 +9,7 @@ import pytestqt.qtbot @pytest.fixture def zoom(qtbot: pytestqt.qtbot.QtBot) -> Zoom: - """Fixture providing a Percentage widget.""" + """Fixture providing a Zoom widget.""" widget = Zoom() qtbot.add_widget(widget) return widget From b59d07231f5953bff51f0d35d03f77b2ab2ef427 Mon Sep 17 00:00:00 2001 From: Skeptic Spriggan Date: Thu, 6 Nov 2025 21:00:10 +0100 Subject: [PATCH 05/11] feat: show zoom percentage in statusbar resolving #2870 --- qutebrowser/browser/webengine/webenginetab.py | 14 +++++++++++++- qutebrowser/mainwindow/statusbar/zoom.py | 4 +--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 90965d217..809b02cb2 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -728,9 +728,21 @@ class WebEngineZoom(browsertab.AbstractZoom): _widget: webview.WebEngineView + def connect_signals(self): + """Called from WebEngineTab.connect_signals.""" + page = self._widget.page() + try: + page.zoomFactorChanged.connect(self.factor_changed) + if machinery.IS_QT6: + self._widget.zoomFactorChanged.connect(self.factor_changed) + except AttributeError: + # Added in Qt 6.8 + pass + def _set_factor_internal(self, factor): self._widget.setZoomFactor(factor) - self.factor_changed.emit(factor) + if not hasattr(self._widget.page(), "zoomFactorChanged"): + self.factor_changed.emit(factor) class WebEngineElements(browsertab.AbstractElements): diff --git a/qutebrowser/mainwindow/statusbar/zoom.py b/qutebrowser/mainwindow/statusbar/zoom.py index af54a38f6..8affa58ea 100644 --- a/qutebrowser/mainwindow/statusbar/zoom.py +++ b/qutebrowser/mainwindow/statusbar/zoom.py @@ -21,6 +21,4 @@ class Zoom(textbase.TextBase): def on_tab_changed(self, tab: browsertab.AbstractTab) -> None: """Update percentage when tab changed.""" - percentage = int(100 * tab.zoom.factor()) - - self.setText(f"{percentage}%") + self.on_zoom_changed(tab.zoom.factor()) From e5fb34d54068aef3f8001603578fc31817fb1cbf Mon Sep 17 00:00:00 2001 From: Skeptic Spriggan Date: Thu, 6 Nov 2025 21:32:26 +0100 Subject: [PATCH 06/11] feat: show zoom percentage in statusbar resolving #2870 --- qutebrowser/browser/webengine/webenginetab.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 809b02cb2..9e06484f8 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -732,9 +732,10 @@ class WebEngineZoom(browsertab.AbstractZoom): """Called from WebEngineTab.connect_signals.""" page = self._widget.page() try: - page.zoomFactorChanged.connect(self.factor_changed) - if machinery.IS_QT6: - self._widget.zoomFactorChanged.connect(self.factor_changed) + if machinery.IS_QT5: + page.zoomFactorChanged.connect(self.factor_changed) # type: ignore[attr-defined] + else: + page.zoomFactorChanged.connect(self.factor_changed) except AttributeError: # Added in Qt 6.8 pass From 9567a75bfb90e2513e0196b33f7c98ebb25d4f2a Mon Sep 17 00:00:00 2001 From: Skeptic Spriggan Date: Fri, 7 Nov 2025 10:25:28 +0100 Subject: [PATCH 07/11] feat: show zoom percentage in statusbar resolving #2870 --- qutebrowser/browser/webengine/webenginetab.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 9e06484f8..a7d0ad6ef 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -731,14 +731,12 @@ class WebEngineZoom(browsertab.AbstractZoom): def connect_signals(self): """Called from WebEngineTab.connect_signals.""" page = self._widget.page() - try: - if machinery.IS_QT5: - page.zoomFactorChanged.connect(self.factor_changed) # type: ignore[attr-defined] - else: + if machinery.IS_QT6: + try: page.zoomFactorChanged.connect(self.factor_changed) - except AttributeError: - # Added in Qt 6.8 - pass + except AttributeError: + # Added in Qt 6.8 + pass def _set_factor_internal(self, factor): self._widget.setZoomFactor(factor) From e5657eb0468a47af43d7793c493c81947fbe2136 Mon Sep 17 00:00:00 2001 From: Skeptic Spriggan Date: Fri, 7 Nov 2025 13:56:29 +0100 Subject: [PATCH 08/11] feat: show zoom percentage in statusbar resolving #2870 --- qutebrowser/browser/webengine/webenginetab.py | 1 + qutebrowser/config/configdata.yml | 11 ++++++- qutebrowser/mainwindow/statusbar/zoom.py | 8 +++-- tests/unit/mainwindow/statusbar/test_zoom.py | 29 ++++++++++++------- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index a7d0ad6ef..c84267252 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -1772,5 +1772,6 @@ class WebEngineTab(browsertab.AbstractTab): self.audio._connect_signals() self.search.connect_signals() self.printing.connect_signals() + self.zoom.connect_signals() self._permissions.connect_signals() self._scripts.connect_signals() diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index 7b1bfbc84..ced9d51cd 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -2197,9 +2197,18 @@ statusbar.widgets: Python datetime documentation]." - zoom: "Display zoom percentage (e.g. `200%`)" none_ok: true - default: ['keypress', 'search_match', 'url', 'scroll', 'history', 'tabs', 'progress'] + default: ['keypress', 'search_match', 'url', 'zoom', 'scroll', 'history', 'tabs', 'progress'] desc: "List of widgets displayed in the statusbar." +statusbar.zoom.show: + default: non-default + type: + name: String + valid_values: + - always: Always show the zoom percentage. + - non-default: Show the zoom percentage when it is not 100%. + desc: When to show the zoom percentage in the statusbar. + ## tabs tabs.background: diff --git a/qutebrowser/mainwindow/statusbar/zoom.py b/qutebrowser/mainwindow/statusbar/zoom.py index 8affa58ea..32749c9b4 100644 --- a/qutebrowser/mainwindow/statusbar/zoom.py +++ b/qutebrowser/mainwindow/statusbar/zoom.py @@ -3,6 +3,7 @@ from qutebrowser.browser import browsertab from qutebrowser.mainwindow.statusbar import textbase from qutebrowser.qt.core import pyqtSlot, QObject +from qutebrowser.config import config class Zoom(textbase.TextBase): @@ -11,13 +12,16 @@ class Zoom(textbase.TextBase): def __init__(self, parent: QObject = None) -> None: super().__init__(parent) - self.setText("100%") + self.on_zoom_changed(1) @pyqtSlot(float) def on_zoom_changed(self, factor: float) -> None: """Update percentage when factor changed.""" + if factor == 1 and config.val.statusbar.zoom.show == 'non-default': + self.setText("") + return percentage = int(100 * factor) - self.setText(f"{percentage}%") + self.setText(f"[{percentage}%]") def on_tab_changed(self, tab: browsertab.AbstractTab) -> None: """Update percentage when tab changed.""" diff --git a/tests/unit/mainwindow/statusbar/test_zoom.py b/tests/unit/mainwindow/statusbar/test_zoom.py index dcc367af5..fdb4334bf 100644 --- a/tests/unit/mainwindow/statusbar/test_zoom.py +++ b/tests/unit/mainwindow/statusbar/test_zoom.py @@ -8,27 +8,34 @@ import pytestqt.qtbot @pytest.fixture -def zoom(qtbot: pytestqt.qtbot.QtBot) -> Zoom: +def zoom(qtbot: pytestqt.qtbot.QtBot, config_stub) -> Zoom: """Fixture providing a Zoom widget.""" widget = Zoom() qtbot.add_widget(widget) return widget -@pytest.mark.parametrize('factor, expected', [ - (1, '100%'), - (1.5, '150%'), - (2, '200%'), - (0.5, '50%'), - (0.25, '25%'), +@pytest.mark.parametrize('factor, show, expected', [ + (1, 'always', '[100%]'), + (1.5, 'always', '[150%]'), + (2, 'always', '[200%]'), + (0.5, 'always', '[50%]'), + (0.25, 'always', '[25%]'), + (1, 'not-default', ''), + (1.5, 'non-default', '[150%]'), + (2, 'non-default', '[200%]'), + (0.5, 'non-default', '[50%]'), + (0.25, 'non-default', '[25%]'), ]) -def test_percentage_texts(zoom: Zoom, factor: float, expected: str) -> None: - """Test text displayed by the widget based on the zoom factor of a tab. +def test_percentage_texts(zoom: Zoom, factor: float, show: str, expected: str, config_stub) -> None: + """Test text displayed by the widget based on the zoom factor of a tab and a config value. Args: factor: zoom factor of the tab as a float. + show: config value for `statusbar.zoom.show`. expected: expected text given factor. """ + config_stub.val.statusbar.zoom.show = show zoom.on_zoom_changed(factor=factor) assert zoom.text() == expected @@ -36,7 +43,7 @@ def test_percentage_texts(zoom: Zoom, factor: float, expected: str) -> None: def test_tab_change(zoom: Zoom, fake_web_tab: Any) -> None: """Test zoom factor change when switching tabs.""" zoom.on_zoom_changed(factor=2) - assert zoom.text() == '200%' + assert zoom.text() == '[200%]' tab = fake_web_tab(zoom_factor=0.5) zoom.on_tab_changed(tab) - assert zoom.text() == '50%' + assert zoom.text() == '[50%]' From d6c768c9b08d14f86f332333cdcdd665eddb6463 Mon Sep 17 00:00:00 2001 From: Skeptic Spriggan Date: Fri, 7 Nov 2025 14:12:38 +0100 Subject: [PATCH 09/11] feat: show zoom percentage in statusbar resolving #2870 --- qutebrowser/browser/webengine/webenginetab.py | 1 + tests/unit/mainwindow/statusbar/test_zoom.py | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index c84267252..7ebd4238a 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -1298,6 +1298,7 @@ class WebEngineTab(browsertab.AbstractTab): search: WebEngineSearch audio: WebEngineAudio printing: WebEnginePrinting + zoom: WebEngineZoom def __init__(self, *, win_id, mode_manager, private, parent=None): super().__init__(win_id=win_id, diff --git a/tests/unit/mainwindow/statusbar/test_zoom.py b/tests/unit/mainwindow/statusbar/test_zoom.py index fdb4334bf..0355f1d52 100644 --- a/tests/unit/mainwindow/statusbar/test_zoom.py +++ b/tests/unit/mainwindow/statusbar/test_zoom.py @@ -8,7 +8,7 @@ import pytestqt.qtbot @pytest.fixture -def zoom(qtbot: pytestqt.qtbot.QtBot, config_stub) -> Zoom: +def zoom(qtbot: pytestqt.qtbot.QtBot, config_stub: Any) -> Zoom: """Fixture providing a Zoom widget.""" widget = Zoom() qtbot.add_widget(widget) @@ -21,13 +21,14 @@ def zoom(qtbot: pytestqt.qtbot.QtBot, config_stub) -> Zoom: (2, 'always', '[200%]'), (0.5, 'always', '[50%]'), (0.25, 'always', '[25%]'), - (1, 'not-default', ''), + (1, 'non-default', ''), (1.5, 'non-default', '[150%]'), (2, 'non-default', '[200%]'), (0.5, 'non-default', '[50%]'), (0.25, 'non-default', '[25%]'), ]) -def test_percentage_texts(zoom: Zoom, factor: float, show: str, expected: str, config_stub) -> None: +def test_percentage_texts(zoom: Zoom, factor: float, show: str, expected: str, + config_stub: Any) -> None: """Test text displayed by the widget based on the zoom factor of a tab and a config value. Args: From 395bdfe2b1bb2949d38940d7088a71f9dbe15e15 Mon Sep 17 00:00:00 2001 From: Skeptic Spriggan Date: Fri, 7 Nov 2025 14:31:06 +0100 Subject: [PATCH 10/11] feat: show zoom percentage in statusbar resolving #2870 --- qutebrowser/config/configdata.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index ced9d51cd..31ba82a29 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -2197,7 +2197,8 @@ statusbar.widgets: Python datetime documentation]." - zoom: "Display zoom percentage (e.g. `200%`)" none_ok: true - default: ['keypress', 'search_match', 'url', 'zoom', 'scroll', 'history', 'tabs', 'progress'] + default: ['keypress', 'search_match', 'url', 'zoom', 'scroll', + 'history', 'tabs', 'progress'] desc: "List of widgets displayed in the statusbar." statusbar.zoom.show: From bc42d5c3bed0e9161292a2af494b689804cc3b69 Mon Sep 17 00:00:00 2001 From: Skeptic Spriggan Date: Sat, 8 Nov 2025 00:06:19 +0100 Subject: [PATCH 11/11] feat: show zoom percentage in statusbar resolving #2870 --- qutebrowser/mainwindow/statusbar/bar.py | 2 +- qutebrowser/mainwindow/statusbar/zoom.py | 5 +-- tests/unit/mainwindow/statusbar/test_zoom.py | 38 ++++++++++++++------ 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/qutebrowser/mainwindow/statusbar/bar.py b/qutebrowser/mainwindow/statusbar/bar.py index 63fd144a3..e7c6016f8 100644 --- a/qutebrowser/mainwindow/statusbar/bar.py +++ b/qutebrowser/mainwindow/statusbar/bar.py @@ -252,7 +252,7 @@ class StatusBar(QWidget): if segment == 'scroll_raw': widget.set_raw() - elif segment in ('history', 'progress'): + elif segment in ('history', 'progress', 'zoom'): widget.enabled = True if tab: widget.on_tab_changed(tab) diff --git a/qutebrowser/mainwindow/statusbar/zoom.py b/qutebrowser/mainwindow/statusbar/zoom.py index 32749c9b4..f24c4d60d 100644 --- a/qutebrowser/mainwindow/statusbar/zoom.py +++ b/qutebrowser/mainwindow/statusbar/zoom.py @@ -18,9 +18,10 @@ class Zoom(textbase.TextBase): def on_zoom_changed(self, factor: float) -> None: """Update percentage when factor changed.""" if factor == 1 and config.val.statusbar.zoom.show == 'non-default': - self.setText("") + self.hide() return - percentage = int(100 * factor) + self.show() + percentage = round(100 * factor) self.setText(f"[{percentage}%]") def on_tab_changed(self, tab: browsertab.AbstractTab) -> None: diff --git a/tests/unit/mainwindow/statusbar/test_zoom.py b/tests/unit/mainwindow/statusbar/test_zoom.py index 0355f1d52..d7f3eb80f 100644 --- a/tests/unit/mainwindow/statusbar/test_zoom.py +++ b/tests/unit/mainwindow/statusbar/test_zoom.py @@ -15,18 +15,17 @@ def zoom(qtbot: pytestqt.qtbot.QtBot, config_stub: Any) -> Zoom: return widget -@pytest.mark.parametrize('factor, show, expected', [ - (1, 'always', '[100%]'), - (1.5, 'always', '[150%]'), - (2, 'always', '[200%]'), - (0.5, 'always', '[50%]'), - (0.25, 'always', '[25%]'), - (1, 'non-default', ''), - (1.5, 'non-default', '[150%]'), - (2, 'non-default', '[200%]'), - (0.5, 'non-default', '[50%]'), - (0.25, 'non-default', '[25%]'), +@pytest.mark.parametrize('factor, expected', [ + (0.25, '[25%]'), + (0.5, '[50%]'), + (0.75, '[75%]'), + (1.5, '[150%]'), + (2, '[200%]'), + (3, '[300%]'), + (4, '[400%]'), + (5, '[500%]'), ]) +@pytest.mark.parametrize("show", ["non-default", "always"]) def test_percentage_texts(zoom: Zoom, factor: float, show: str, expected: str, config_stub: Any) -> None: """Test text displayed by the widget based on the zoom factor of a tab and a config value. @@ -41,6 +40,23 @@ def test_percentage_texts(zoom: Zoom, factor: float, show: str, expected: str, assert zoom.text() == expected +@pytest.mark.parametrize('show, expected', [ + ("always", '[100%]'), + ("non-default", ''), +]) +def test_default_percentage_text(zoom: Zoom, show: str, expected: str, + config_stub: Any) -> None: + """Test default percentage text based on a config value. + + Args: + show: config value for `statusbar.zoom.show`. + expected: expected text given show config value. + """ + config_stub.val.statusbar.zoom.show = show + zoom.on_zoom_changed(factor=1) + assert zoom.text() == expected + + def test_tab_change(zoom: Zoom, fake_web_tab: Any) -> None: """Test zoom factor change when switching tabs.""" zoom.on_zoom_changed(factor=2)