Move mouse handling to an EventFilter

This commit is contained in:
Florian Bruhin 2016-08-10 16:37:52 +02:00
parent 85a4dc808e
commit f908d29a5f
7 changed files with 128 additions and 41 deletions

View File

@ -29,6 +29,7 @@ from qutebrowser.keyinput import modeman
from qutebrowser.config import config
from qutebrowser.utils import utils, objreg, usertypes, message, log, qtutils
from qutebrowser.misc import miscwidgets
from qutebrowser.browser import mouse
tab_id_gen = itertools.count(0)
@ -481,8 +482,12 @@ class AbstractTab(QWidget):
self._progress = 0
self._has_ssl_errors = False
self._load_status = usertypes.LoadStatus.none
self._mouse_event_filter = mouse.MouseEventFilter(self, parent=self)
self.backend = None
def _event_filter_target(self):
raise NotImplementedError
def _set_widget(self, widget):
# pylint: disable=protected-access
self._widget = widget
@ -494,6 +499,8 @@ class AbstractTab(QWidget):
self.search._widget = widget
self.printing._widget = widget
widget.mouse_wheel_zoom.connect(self.zoom._on_mouse_wheel_zoom)
event_filter_target = self._event_filter_target()
event_filter_target.installEventFilter(self._mouse_event_filter)
def _set_load_status(self, val):
"""Setter for load_status."""

View File

@ -0,0 +1,77 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Mouse handling for a browser tab."""
from qutebrowser.config import config
from qutebrowser.utils import message
from PyQt5.QtCore import QObject, QEvent, Qt
class MouseEventFilter(QObject):
"""Handle mouse events on a tab."""
def __init__(self, tab, parent=None):
super().__init__(parent)
self._tab = tab
self._handlers = {
QEvent.MouseButtonPress: self._handle_mouse_press,
}
def _handle_mouse_press(self, e):
"""Handle pressing of a mouse button."""
is_rocker_gesture = (config.get('input', 'rocker-gestures') and
e.buttons() == Qt.LeftButton | Qt.RightButton)
if e.button() in [Qt.XButton1, Qt.XButton2] or is_rocker_gesture:
self._mousepress_backforward(e)
return True
return False
def _mousepress_backforward(self, e):
"""Handle back/forward mouse button presses.
Args:
e: The QMouseEvent.
"""
if e.button() in [Qt.XButton1, Qt.LeftButton]:
# Back button on mice which have it, or rocker gesture
if self._tab.history.can_go_back():
self._tab.history.back()
else:
message.error(self._tab.win_id, "At beginning of history.",
immediately=True)
elif e.button() in [Qt.XButton2, Qt.RightButton]:
# Forward button on mice which have it, or rocker gesture
if self._tab.history.can_go_forward():
self._tab.history.forward()
else:
message.error(self._tab.win_id, "At end of history.",
immediately=True)
def eventFilter(self, _obj, event):
"""Filter events going to a QWeb(Engine)View."""
evtype = event.type()
if evtype not in self._handlers:
return False
return self._handlers[evtype](event)

View File

@ -359,6 +359,9 @@ class WebEngineTab(browsertab.AbstractTab):
# FIXME:qtwebengine what about runsOnSubFrames?
page.scripts().insert(script)
def _event_filter_target(self):
return self._widget.focusProxy()
def openurl(self, url):
self._openurl_prepare(url)
self._widget.load(url)

View File

@ -510,6 +510,9 @@ class WebKitTab(browsertab.AbstractTab):
self.zoom.set_default()
self.backend = usertypes.Backend.QtWebKit
def _event_filter_target(self):
return self._widget
def openurl(self, url):
self._openurl_prepare(url)
self._widget.openurl(url)

View File

@ -29,7 +29,7 @@ from PyQt5.QtWebKitWidgets import QWebView, QWebPage, QWebFrame
from qutebrowser.config import config
from qutebrowser.keyinput import modeman
from qutebrowser.utils import message, log, usertypes, utils, qtutils, objreg
from qutebrowser.utils import log, usertypes, utils, qtutils, objreg
from qutebrowser.browser import hints
from qutebrowser.browser.webkit import webpage, webkitelem
@ -138,27 +138,6 @@ class WebView(QWebView):
elif section == 'colors' and option == 'webpage.bg':
self._set_bg_color()
def _mousepress_backforward(self, e):
"""Handle back/forward mouse button presses.
Args:
e: The QMouseEvent.
"""
if e.button() in [Qt.XButton1, Qt.LeftButton]:
# Back button on mice which have it, or rocker gesture
if self.page().history().canGoBack():
self.back()
else:
message.error(self.win_id, "At beginning of history.",
immediately=True)
elif e.button() in [Qt.XButton2, Qt.RightButton]:
# Forward button on mice which have it, or rocker gesture
if self.page().history().canGoForward():
self.forward()
else:
message.error(self.win_id, "At end of history.",
immediately=True)
def _mousepress_insertmode(self, e):
"""Switch to insert mode when an editable element was clicked.
@ -407,13 +386,6 @@ class WebView(QWebView):
Return:
The superclass return value.
"""
is_rocker_gesture = (config.get('input', 'rocker-gestures') and
e.buttons() == Qt.LeftButton | Qt.RightButton)
if e.button() in [Qt.XButton1, Qt.XButton2] or is_rocker_gesture:
self._mousepress_backforward(e)
super().mousePressEvent(e)
return
self._mousepress_insertmode(e)
self._mousepress_opentarget(e)
self._ignore_wheel_event = True

16
tests/manual/mouse.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Mouse control</title>
</head>
<body>
<ul>
<li>Middle- or Ctrl-click on a <a href="https://www.qutebrowser.org">link</a> should open it in a new tab (fg/bg according to <code>tabs -> background-tabs</code>)</li>
<li>When clicking the link with shift, <code>background-tabs</code> should be reversed accordingly.</li>
<li>Ctrl + Mousewheel should zoom in/out</li>
<li>Back/forward keys on mouse should navigate back/forward</li>
<li>With <code>input -> rocker-gestures</code> set, no context menu should be shown, but pressing left+right/right+left buttons should navigate back/forward</li>
<li>When setting <code>input -> rocker-gestures</code> dynamically, the context menu should be hidden/shown accordingly.</li>
</body>
</html>

View File

@ -91,26 +91,35 @@ def tab(request, default_config, qtbot, tab_registry, cookiejar_and_cache):
objreg.delete('mode-manager', scope='window', window=0)
class Tab(browsertab.AbstractTab):
# pylint: disable=abstract-method
def __init__(self, win_id, parent=None):
super().__init__(win_id, parent)
mode_manager = modeman.ModeManager(0)
self.history = browsertab.AbstractHistory(self)
self.scroller = browsertab.AbstractScroller(self, parent=self)
self.caret = browsertab.AbstractCaret(win_id=self.win_id,
mode_manager=mode_manager,
tab=self, parent=self)
self.zoom = browsertab.AbstractZoom(win_id=self.win_id)
self.search = browsertab.AbstractSearch(parent=self)
self.printing = browsertab.AbstractPrinting()
def _event_filter_target(self):
return self._widget
@pytest.mark.skipif(PYQT_VERSION < 0x050600,
reason='Causes segfaults, see #1638')
def test_tab(qtbot, view, config_stub, tab_registry):
tab_w = browsertab.AbstractTab(win_id=0)
tab_w = Tab(win_id=0)
qtbot.add_widget(tab_w)
assert tab_w.win_id == 0
assert tab_w._widget is None
mode_manager = modeman.ModeManager(0)
tab_w.history = browsertab.AbstractHistory(tab_w)
tab_w.scroller = browsertab.AbstractScroller(tab_w, parent=tab_w)
tab_w.caret = browsertab.AbstractCaret(win_id=tab_w.win_id,
mode_manager=mode_manager,
tab=tab_w, parent=tab_w)
tab_w.zoom = browsertab.AbstractZoom(win_id=tab_w.win_id)
tab_w.search = browsertab.AbstractSearch(parent=tab_w)
tab_w.printing = browsertab.AbstractPrinting()
tab_w._set_widget(view)
assert tab_w._widget is view
assert tab_w.history._tab is tab_w