This commit is contained in:
skepticspriggan 2026-01-07 16:35:38 -08:00 committed by GitHub
commit ce38941742
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 155 additions and 5 deletions

View File

@ -82,6 +82,7 @@ Added
* Loaded WebExtensions (partial support landed in QtWebEngine 6.10, no
official qutebrowser support yet).
- Support for hinting elements which are part of an (open) shadow DOM.
- Show zoom percentage in statusbar. (#2870)
Changed
~~~~~~~

View File

@ -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

View File

@ -728,8 +728,20 @@ class WebEngineZoom(browsertab.AbstractZoom):
_widget: webview.WebEngineView
def connect_signals(self):
"""Called from WebEngineTab.connect_signals."""
page = self._widget.page()
if machinery.IS_QT6:
try:
page.zoomFactorChanged.connect(self.factor_changed)
except AttributeError:
# Added in Qt 6.8
pass
def _set_factor_internal(self, factor):
self._widget.setZoomFactor(factor)
if not hasattr(self._widget.page(), "zoomFactorChanged"):
self.factor_changed.emit(factor)
class WebEngineElements(browsertab.AbstractElements):
@ -1286,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,
@ -1760,5 +1773,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()

View File

@ -2208,10 +2208,21 @@ 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']
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:

View File

@ -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)

View File

@ -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)
@ -248,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)
@ -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]
@ -423,6 +428,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

View File

@ -0,0 +1,29 @@
"""Zoom percentage displayed in the statusbar."""
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):
"""Shows zoom percentage in current tab."""
def __init__(self, parent: QObject = None) -> None:
super().__init__(parent)
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.hide()
return
self.show()
percentage = round(100 * factor)
self.setText(f"[{percentage}%]")
def on_tab_changed(self, tab: browsertab.AbstractTab) -> None:
"""Update percentage when tab changed."""
self.on_zoom_changed(tab.zoom.factor())

View File

@ -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(

View File

@ -205,6 +205,18 @@ class FakeWebTabScroller(browsertab.AbstractScroller):
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):
"""Fake for Web{Kit,Engine}History."""
@ -245,7 +257,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 +267,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()

View File

@ -0,0 +1,66 @@
"""Test Zoom widget."""
import pytest
from qutebrowser.mainwindow.statusbar.zoom import Zoom
from typing import Any
import pytestqt.qtbot
@pytest.fixture
def zoom(qtbot: pytestqt.qtbot.QtBot, config_stub: Any) -> Zoom:
"""Fixture providing a Zoom widget."""
widget = Zoom()
qtbot.add_widget(widget)
return widget
@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.
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
@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)
assert zoom.text() == '[200%]'
tab = fake_web_tab(zoom_factor=0.5)
zoom.on_tab_changed(tab)
assert zoom.text() == '[50%]'