Fix various zooming issues

This commit is contained in:
Florian Bruhin 2016-06-15 13:02:24 +02:00
parent edb65ecf50
commit 16c397a9d2
7 changed files with 136 additions and 96 deletions

View File

@ -354,7 +354,7 @@ class CommandDispatcher:
if config.get('tabs', 'tabs-are-windows'):
new_tabbed_browser.window().setWindowIcon(curtab.icon())
newtab.keep_icon = True
newtab.set_zoom_factor(curtab.zoom_factor())
newtab.zoom.set_factor(curtab.zoom.factor())
newtab.history.deserialize(curtab.history.serialize())
return newtab
@ -663,7 +663,7 @@ class CommandDispatcher:
"""
tab = self._current_widget()
try:
perc = tab.zoom(count)
perc = tab.zoom.offset(count)
except ValueError as e:
raise cmdexc.CommandError(e)
message.info(self._win_id, "Zoom level: {}%".format(perc))
@ -678,7 +678,7 @@ class CommandDispatcher:
"""
tab = self._current_widget()
try:
perc = tab.zoom(-count)
perc = tab.zoom.offset(-count)
except ValueError as e:
raise cmdexc.CommandError(e)
message.info(self._win_id, "Zoom level: {}%".format(perc))
@ -703,9 +703,9 @@ class CommandDispatcher:
tab = self._current_widget()
try:
tab.zoom_perc(level)
except ValueError as e:
raise cmdexc.CommandError(e)
tab.zoom.set_factor(float(level) / 100)
except ValueError:
raise cmdexc.CommandError("Can't zoom {}%!".format(level))
message.info(self._win_id, "Zoom level: {}%".format(level))
@cmdutils.register(instance='command-dispatcher', scope='window')

View File

@ -21,11 +21,12 @@
import itertools
from PyQt5.QtCore import pyqtSignal, QUrl, QObject
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QPoint
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QWidget, QLayout
from qutebrowser.utils import utils, objreg
from qutebrowser.config import config
from qutebrowser.utils import utils, objreg, usertypes
tab_id_gen = itertools.count(0)
@ -55,6 +56,94 @@ class WrapperLayout(QLayout):
self._widget.setGeometry(r)
class AbstractZoom(QObject):
"""Attribute of AbstractTab for controlling zoom.
Attributes:
_neighborlist: A NeighborList with the zoom levels.
_default_zoom_changed: Whether the zoom was changed from the default.
"""
def __init__(self, win_id, parent=None):
super().__init__(parent)
self.widget = None
self._win_id = win_id
self._default_zoom_changed = False
self._init_neighborlist()
objreg.get('config').changed.connect(self.on_config_changed)
# # FIXME is this needed?
# # For some reason, this signal doesn't get disconnected automatically
# # when the WebView is destroyed on older PyQt versions.
# # See https://github.com/The-Compiler/qutebrowser/issues/390
# self.destroyed.connect(functools.partial(
# cfg.changed.disconnect, self.init_neighborlist))
def _set_default_zoom(self):
default_zoom = config.get('ui', 'default-zoom')
self._set_factor_internal(float(default_zoom) / 100)
@pyqtSlot(str, str)
def on_config_changed(self, section, option):
if section == 'ui' and option in ('zoom-levels', 'default-zoom'):
if not self._default_zoom_changed:
factor = float(config.get('ui', 'default-zoom')) / 100
self._set_factor_internal(factor)
self._default_zoom_changed = False
self._init_neighborlist()
def _init_neighborlist(self):
"""Initialize self._neighborlist."""
levels = config.get('ui', 'zoom-levels')
self._neighborlist = usertypes.NeighborList(
levels, mode=usertypes.NeighborList.Modes.edge)
self._neighborlist.fuzzyval = config.get('ui', 'default-zoom')
def offset(self, offset):
"""Increase/Decrease the zoom level by the given offset.
Args:
offset: The offset in the zoom level list.
Return:
The new zoom percentage.
"""
level = self._neighborlist.getitem(offset)
self.set_factor(float(level) / 100, fuzzyval=False)
return level
def set_factor(self, factor, *, fuzzyval=True):
"""Zoom to a given zoom factor.
Args:
factor: The zoom factor as float.
fuzzyval: Whether to set the NeighborLists fuzzyval.
"""
if fuzzyval:
self._neighborlist.fuzzyval = int(factor * 100)
if factor < 0:
raise ValueError("Can't zoom to factor {}!".format(factor))
self._default_zoom_changed = True
self._set_factor_internal(factor)
def factor(self):
raise NotImplementedError
@pyqtSlot(QPoint)
def on_mouse_wheel_zoom(self, delta):
"""Handle zooming via mousewheel requested by the web view."""
divider = config.get('input', 'mouse-zoom-divider')
factor = self.zoomFactor() + delta.y() / divider
if factor < 0:
return
perc = int(100 * factor)
message.info(self.win_id, "Zoom level: {}%".format(perc))
self._neighborlist.fuzzyval = perc
self._set_factor_internal(factor)
self._default_zoom_changed = True
class AbstractCaret(QObject):
"""Attribute of AbstractTab for caret browsing."""
@ -262,6 +351,7 @@ class AbstractTab(QWidget):
# self.history = AbstractHistory(self)
# self.scroll = AbstractScroller(parent=self)
# self.caret = AbstractCaret(win_id=win_id, tab=self, parent=self)
# self.zoom = AbstractZoom(win_id=win_id)
self._layout = None
self._widget = None
self.keep_icon = False # FIXME:refactor get rid of this?
@ -272,6 +362,8 @@ class AbstractTab(QWidget):
self.history.history = widget.history()
self.scroll.widget = widget
self.caret.widget = widget
self.zoom.widget = widget
widget.mouse_wheel_zoom.connect(self.zoom.on_mouse_wheel_zoom)
widget.setParent(self)
@property
@ -317,12 +409,6 @@ class AbstractTab(QWidget):
def title(self):
raise NotImplementedError
def set_zoom_factor(self, factor):
raise NotImplementedError
def zoom_factor(self):
raise NotImplementedError
def icon(self):
raise NotImplementedError

View File

@ -76,6 +76,15 @@ class WebEngineHistory(tab.AbstractHistory):
raise NotImplementedError
class WebEngineZoom(tab.AbstractZoom):
def _set_factor_internal(self, factor):
self.widget.setZoomFactor(factor)
def factor(self):
return self.widget.zoomFactor()
class WebEngineViewTab(tab.AbstractTab):
def __init__(self, win_id, parent=None):
@ -84,6 +93,7 @@ class WebEngineViewTab(tab.AbstractTab):
self.history = WebEngineHistory(self)
self.scroll = WebEngineScroller()
self.caret = WebEngineCaret(win_id=win_id, tab=self, parent=self)
self.zoom = WebEngineZoom(win_id=win_id, parent=self)
self._set_widget(widget)
self._connect_signals()
@ -102,12 +112,6 @@ class WebEngineViewTab(tab.AbstractTab):
def load_status(self):
return usertypes.LoadStatus.success
def set_zoom_factor(self, factor):
self._widget.setZoomFactor(factor)
def zoom_factor(self):
return self._widget.zoomFactor()
def dump_async(self, callback, *, plain=False):
if plain:
self._widget.page().toPlainText(callback)

View File

@ -213,6 +213,15 @@ class WebViewCaret(tab.AbstractCaret):
self.widget.triggerPageAction(QWebPage.MoveToNextChar)
class WebViewZoom(tab.AbstractZoom):
def _set_factor_internal(self, factor):
self.widget.setZoomFactor(factor)
def factor(self):
return self.widget.zoomFactor()
class WebViewScroller(tab.AbstractScroller):
def pos_px(self):
@ -338,7 +347,7 @@ class WebViewHistory(tab.AbstractHistory):
cur_data = self.history.currentItem().userData()
if cur_data is not None:
if 'zoom' in cur_data:
self.tab.zoom_perc(cur_data['zoom'] * 100)
self.tab.zoom.set_factor(cur_data['zoom'])
if ('scroll-pos' in cur_data and
self.tab.scroll.pos_px() == QPoint(0, 0)):
QTimer.singleShot(0, functools.partial(
@ -349,12 +358,14 @@ class WebViewTab(tab.AbstractTab):
def __init__(self, win_id, parent=None):
super().__init__(win_id)
widget = webview.WebView(win_id, self.tab_id)
widget = webview.WebView(win_id, self.tab_id, tab=self)
self.history = WebViewHistory(self)
self.scroll = WebViewScroller(parent=self)
self.caret = WebViewCaret(win_id=win_id, tab=self, parent=self)
self.zoom = WebViewZoom(win_id=win_id, parent=self)
self._set_widget(widget)
self._connect_signals()
self.zoom._set_default_zoom()
def openurl(self, url):
self._widget.openurl(url)
@ -402,12 +413,6 @@ class WebViewTab(tab.AbstractTab):
def title(self):
return self._widget.title()
def set_zoom_factor(self, factor):
self._widget.setZoomFactor(factor)
def zoom_factor(self):
return self._widget.zoomFactor()
def has_selection(self):
return self._widget.hasSelection()

View File

@ -419,7 +419,7 @@ class BrowserPage(QWebPage):
if data is None:
return
if 'zoom' in data:
frame.page().view().zoom_perc(data['zoom'] * 100)
frame.page().view().tab.zoom.set_factor(data['zoom'])
if 'scroll-pos' in data and frame.scrollPosition() == QPoint(0, 0):
frame.setScrollPosition(data['scroll-pos'])

View File

@ -23,7 +23,7 @@ import sys
import itertools
import functools
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QTimer, QUrl
from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QTimer, QUrl, QPoint
from PyQt5.QtGui import QPalette
from PyQt5.QtWidgets import QApplication, QStyleFactory
from PyQt5.QtWebKit import QWebSettings
@ -43,6 +43,7 @@ class WebView(QWebView):
Our own subclass of a QWebView with some added bells and whistles.
Attributes:
tab: The WebKitTab object for this WebView
hintmanager: The HintManager instance for this view.
progress: loading progress of this page.
scroll_pos: The current scroll position as (x%, y%) tuple.
@ -57,11 +58,9 @@ class WebView(QWebView):
search_flags: The search flags of the last search.
_tab_id: The tab ID of the view.
_has_ssl_errors: Whether SSL errors occurred during loading.
_zoom: A NeighborList with the zoom levels.
_old_scroll_pos: The old scroll position.
_check_insertmode: If True, in mouseReleaseEvent we should check if we
need to enter/leave insert mode.
_default_zoom_changed: Whether the zoom was changed from the default.
_ignore_wheel_event: Ignore the next wheel event.
See https://github.com/The-Compiler/qutebrowser/issues/395
@ -72,6 +71,9 @@ class WebView(QWebView):
linkHovered: QWebPages linkHovered signal exposed.
load_status_changed: The loading status changed
url_text_changed: Current URL string changed.
mouse_wheel_zoom: Emitted when the page should be zoomed because the
mousewheel was used with ctrl.
arg 1: The angle delta of the wheel event (QPoint)
shutting_down: Emitted when the view is shutting down.
"""
@ -80,13 +82,15 @@ class WebView(QWebView):
load_status_changed = pyqtSignal(str)
url_text_changed = pyqtSignal(str)
shutting_down = pyqtSignal()
mouse_wheel_zoom = pyqtSignal(QPoint)
def __init__(self, win_id, tab_id, parent=None):
def __init__(self, win_id, tab_id, tab, parent=None):
super().__init__(parent)
if sys.platform == 'darwin' and qtutils.version_check('5.4'):
# WORKAROUND for https://bugreports.qt.io/browse/QTBUG-42948
# See https://github.com/The-Compiler/qutebrowser/issues/462
self.setStyle(QStyleFactory.create('Fusion'))
self.tab = tab
self.win_id = win_id
self.load_status = usertypes.LoadStatus.none
self._check_insertmode = False
@ -94,21 +98,12 @@ class WebView(QWebView):
self.scroll_pos = (-1, -1)
self.statusbar_message = ''
self._old_scroll_pos = (-1, -1)
self._zoom = None
self._has_ssl_errors = False
self._ignore_wheel_event = False
self.keep_icon = False
self.search_text = None
self.search_flags = 0
self.init_neighborlist()
self._set_bg_color()
cfg = objreg.get('config')
cfg.changed.connect(self.init_neighborlist)
# For some reason, this signal doesn't get disconnected automatically
# when the WebView is destroyed on older PyQt versions.
# See https://github.com/The-Compiler/qutebrowser/issues/390
self.destroyed.connect(functools.partial(
cfg.changed.disconnect, self.init_neighborlist))
self.cur_url = QUrl()
self.progress = 0
self.registry = objreg.ObjectRegistry()
@ -129,8 +124,6 @@ class WebView(QWebView):
mode_manager.entered.connect(self.on_mode_entered)
mode_manager.left.connect(self.on_mode_left)
self.viewing_source = False
self.setZoomFactor(float(config.get('ui', 'default-zoom')) / 100)
self._default_zoom_changed = False
if config.get('input', 'rocker-gestures'):
self.setContextMenuPolicy(Qt.PreventContextMenu)
self.urlChanged.connect(self.on_url_changed)
@ -201,14 +194,8 @@ class WebView(QWebView):
@pyqtSlot(str, str)
def on_config_changed(self, section, option):
"""Reinitialize the zoom neighborlist if related config changed."""
if section == 'ui' and option in ('zoom-levels', 'default-zoom'):
if not self._default_zoom_changed:
self.setZoomFactor(float(config.get('ui', 'default-zoom')) /
100)
self._default_zoom_changed = False
self.init_neighborlist()
elif section == 'input' and option == 'rocker-gestures':
"""Update rocker gestures/background color."""
if section == 'input' and option == 'rocker-gestures':
if config.get('input', 'rocker-gestures'):
self.setContextMenuPolicy(Qt.PreventContextMenu)
else:
@ -216,13 +203,6 @@ class WebView(QWebView):
elif section == 'colors' and option == 'webpage.bg':
self._set_bg_color()
def init_neighborlist(self):
"""Initialize the _zoom neighborlist."""
levels = config.get('ui', 'zoom-levels')
self._zoom = usertypes.NeighborList(
levels, mode=usertypes.NeighborList.Modes.edge)
self._zoom.fuzzyval = config.get('ui', 'default-zoom')
def _mousepress_backforward(self, e):
"""Handle back/forward mouse button presses.
@ -372,33 +352,6 @@ class WebView(QWebView):
bridge = objreg.get('js-bridge')
frame.addToJavaScriptWindowObject('qute', bridge)
def zoom_perc(self, perc, fuzzyval=True):
"""Zoom to a given zoom percentage.
Args:
perc: The zoom percentage as int.
fuzzyval: Whether to set the NeighborLists fuzzyval.
"""
if fuzzyval:
self._zoom.fuzzyval = int(perc)
if perc < 0:
raise ValueError("Can't zoom {}%!".format(perc))
self.setZoomFactor(float(perc) / 100)
self._default_zoom_changed = True
def zoom(self, offset):
"""Increase/Decrease the zoom level.
Args:
offset: The offset in the zoom level list.
Return:
The new zoom percentage.
"""
level = self._zoom.getitem(offset)
self.zoom_perc(level, fuzzyval=False)
return level
@pyqtSlot('QUrl')
def on_url_changed(self, url):
"""Update cur_url when URL has changed.
@ -635,14 +588,6 @@ class WebView(QWebView):
return
if e.modifiers() & Qt.ControlModifier:
e.accept()
divider = config.get('input', 'mouse-zoom-divider')
factor = self.zoomFactor() + e.angleDelta().y() / divider
if factor < 0:
return
perc = int(100 * factor)
message.info(self.win_id, "Zoom level: {}%".format(perc))
self._zoom.fuzzyval = perc
self.setZoomFactor(factor)
self._default_zoom_changed = True
self.mouse_wheel_zoom.emit(e.angleDelta())
else:
super().wheelEvent(e)

View File

@ -166,7 +166,7 @@ class SessionManager(QObject):
user_data = item.userData()
if tab.history.current_idx() == idx:
pos = tab.scroll.pos_px()
item_data['zoom'] = tab.zoom_factor()
item_data['zoom'] = tab.zoom.factor()
item_data['scroll-pos'] = {'x': pos.x(), 'y': pos.y()}
elif user_data is not None:
if 'zoom' in user_data: