diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py
index 3069ed71e..7659897c0 100644
--- a/qutebrowser/browser/hints.py
+++ b/qutebrowser/browser/hints.py
@@ -124,8 +124,8 @@ class HintLabel(QLabel):
self.hide()
return
no_js = config.get('hints', 'find-implementation') != 'javascript'
- self.elem.rect_on_view(no_js=no_js,
- callback=lambda r: self.move(r.x(), r.y()))
+ rect = self.elem.rect_on_view(no_js=no_js)
+ self.move(rect.x(), rect.y())
def cleanup(self):
"""Clean up this element and hide it."""
@@ -221,59 +221,54 @@ class HintActions(QObject):
else:
target_mapping[Target.tab] = usertypes.ClickTarget.tab
- def click_cb(rect):
- """Actually click the element.
+ # Click the center of the largest square fitting into the top/left
+ # corner of the rectangle, this will help if part of the element
+ # is hidden behind other elements
+ # https://github.com/The-Compiler/qutebrowser/issues/1005
+ rect = elem.rect_on_view()
+ if rect.width() > rect.height():
+ rect.setWidth(rect.height())
+ else:
+ rect.setHeight(rect.width())
+ pos = rect.center()
- Click the center of the largest square fitting into the top/left
- corner of the rectangle, this will help if part of the
- element is hidden behind other elements
- https://github.com/The-Compiler/qutebrowser/issues/1005
- """
- if rect.width() > rect.height():
- rect.setWidth(rect.height())
- else:
- rect.setHeight(rect.width())
- pos = rect.center()
+ action = "Hovering" if context.target == Target.hover else "Clicking"
+ log.hints.debug("{} on '{}' at position {}".format(
+ action, elem.debug_text(), pos))
- action = "Hovering" if context.target == Target.hover else "Clicking"
- log.hints.debug("{} on '{}' at position {}".format(
- action, elem.debug_text(), pos))
-
- self.start_hinting.emit(target_mapping[context.target])
- if context.target in [Target.tab, Target.tab_fg, Target.tab_bg,
- Target.window]:
- modifiers = Qt.ControlModifier
- else:
- modifiers = Qt.NoModifier
- events = [
- QMouseEvent(QEvent.MouseMove, pos, Qt.NoButton, Qt.NoButton,
- Qt.NoModifier),
+ self.start_hinting.emit(target_mapping[context.target])
+ if context.target in [Target.tab, Target.tab_fg, Target.tab_bg,
+ Target.window]:
+ modifiers = Qt.ControlModifier
+ else:
+ modifiers = Qt.NoModifier
+ events = [
+ QMouseEvent(QEvent.MouseMove, pos, Qt.NoButton, Qt.NoButton,
+ Qt.NoModifier),
+ ]
+ if context.target != Target.hover:
+ events += [
+ QMouseEvent(QEvent.MouseButtonPress, pos, Qt.LeftButton,
+ Qt.LeftButton, modifiers),
+ QMouseEvent(QEvent.MouseButtonRelease, pos, Qt.LeftButton,
+ Qt.NoButton, modifiers),
]
- if context.target != Target.hover:
- events += [
- QMouseEvent(QEvent.MouseButtonPress, pos, Qt.LeftButton,
- Qt.LeftButton, modifiers),
- QMouseEvent(QEvent.MouseButtonRelease, pos, Qt.LeftButton,
- Qt.NoButton, modifiers),
- ]
- if context.target in [Target.normal, Target.current]:
- # Set the pre-jump mark ', so we can jump back here after following
- tabbed_browser = objreg.get('tabbed-browser', scope='window',
- window=self._win_id)
- tabbed_browser.set_mark("'")
+ if context.target in [Target.normal, Target.current]:
+ # Set the pre-jump mark ', so we can jump back here after following
+ tabbed_browser = objreg.get('tabbed-browser', scope='window',
+ window=self._win_id)
+ tabbed_browser.set_mark("'")
- if context.target == Target.current:
- elem.remove_blank_target()
- for evt in events:
- self.mouse_event.emit(evt)
- if elem.is_text_input() and elem.is_editable():
- QTimer.singleShot(0, functools.partial(
- elem.frame().page().triggerAction,
- QWebPage.MoveToEndOfDocument))
- QTimer.singleShot(0, self.stop_hinting.emit)
-
- elem.rect_on_view(callback=click_cb)
+ if context.target == Target.current:
+ elem.remove_blank_target()
+ for evt in events:
+ self.mouse_event.emit(evt)
+ if elem.is_text_input() and elem.is_editable():
+ QTimer.singleShot(0, functools.partial(
+ elem.frame().page().triggerAction,
+ QWebPage.MoveToEndOfDocument))
+ QTimer.singleShot(0, self.stop_hinting.emit)
def yank(self, url, context):
"""Yank an element to the clipboard or primary selection.
diff --git a/qutebrowser/browser/webkit/webkitelem.py b/qutebrowser/browser/webkit/webkitelem.py
index 90d4812d9..75b6559b3 100644
--- a/qutebrowser/browser/webkit/webkitelem.py
+++ b/qutebrowser/browser/webkit/webkitelem.py
@@ -199,22 +199,8 @@ class WebKitElement(webelem.AbstractWebElement):
frame = frame.parentFrame()
return rect
- def _rect_on_view_sync(self, *, elem_geometry=None, no_js=False):
- """Synchronous part of rect_on_view."""
- self._check_vanished()
-
- # First try getting the element rect via JS, as that's usually more
- # accurate
- if elem_geometry is None and not no_js:
- rect = self._rect_on_view_js()
- if rect is not None:
- return rect
-
- # No suitable rects found via JS, try via the QWebElement API
- return self._rect_on_view_python(elem_geometry)
-
- def rect_on_view(self, *, callback, **kwargs):
- """Get the geometry of the element relative to the webview (async).
+ def rect_on_view(self, *, elem_geometry=None, no_js=False):
+ """Get the geometry of the element relative to the webview.
Uses the getClientRects() JavaScript method to obtain the collection of
rectangles containing the element and returns the first rectangle which
@@ -230,15 +216,21 @@ class WebKitElement(webelem.AbstractWebElement):
Calling QWebElement::geometry is rather expensive so
we want to avoid doing it twice.
no_js: Fall back to the Python implementation
- callback: Gets called with the found QRect.
"""
self._check_vanished()
- callback(self._rect_on_view_sync(**kwargs))
+
+ # First try getting the element rect via JS, as that's usually more
+ # accurate
+ if elem_geometry is None and not no_js:
+ rect = self._rect_on_view_js()
+ if rect is not None:
+ return rect
+
+ # No suitable rects found via JS, try via the QWebElement API
+ return self._rect_on_view_python(elem_geometry)
def is_visible(self, mainframe):
"""Check if the given element is visible in the given frame."""
- # FIXME:qtwebengine can we get rid of this with
- # find_all_elements(only_visible=True)?
self._check_vanished()
# CSS attributes which hide an element
hidden_attributes = {
@@ -253,7 +245,7 @@ class WebKitElement(webelem.AbstractWebElement):
# Most likely an invisible link
return False
# First check if the element is visible on screen
- elem_rect = self._rect_on_view_sync(elem_geometry=elem_geometry)
+ elem_rect = self.rect_on_view(elem_geometry=elem_geometry)
mainframe_geometry = mainframe.geometry()
if elem_rect.isValid():
visible_on_screen = mainframe_geometry.intersects(elem_rect)
diff --git a/tests/unit/browser/webkit/test_webkitelem.py b/tests/unit/browser/webkit/test_webkitelem.py
index 443913b3e..fb179c590 100644
--- a/tests/unit/browser/webkit/test_webkitelem.py
+++ b/tests/unit/browser/webkit/test_webkitelem.py
@@ -269,7 +269,7 @@ class TestWebKitElement:
lambda e: e.outer_xml(),
lambda e: e.tag_name(),
lambda e: e.run_js_async(''),
- lambda e: e.rect_on_view(callback=None),
+ lambda e: e.rect_on_view(),
lambda e: e.is_visible(None),
], ids=['str', 'getitem', 'setitem', 'delitem', 'contains', 'iter', 'len',
'frame', 'geometry', 'style_property', 'text', 'set_text',
@@ -708,24 +708,22 @@ class TestRectOnView:
# unusable geometry via getElementRects
{'length': '1', '0': {'width': 0, 'height': 0, 'x': 0, 'y': 0}},
])
- def test_simple(self, callback_checker, stubs, js_rect):
+ def test_simple(self, stubs, js_rect):
geometry = QRect(5, 5, 4, 4)
frame = stubs.FakeWebFrame(QRect(0, 0, 100, 100))
elem = get_webelem(geometry, frame, js_rect_return=js_rect)
- elem.rect_on_view(callback=callback_checker.callback)
- callback_checker.check(QRect(5, 5, 4, 4))
+ assert elem.rect_on_view() == QRect(5, 5, 4, 4)
@pytest.mark.parametrize('js_rect', [None, {}])
- def test_scrolled(self, callback_checker, stubs, js_rect):
+ def test_scrolled(self, stubs, js_rect):
geometry = QRect(20, 20, 4, 4)
frame = stubs.FakeWebFrame(QRect(0, 0, 100, 100),
scroll=QPoint(10, 10))
elem = get_webelem(geometry, frame, js_rect_return=js_rect)
- elem.rect_on_view(callback=callback_checker.callback)
- callback_checker.check(QRect(20 - 10, 20 - 10, 4, 4))
+ assert elem.rect_on_view() == QRect(20 - 10, 20 - 10, 4, 4)
@pytest.mark.parametrize('js_rect', [None, {}])
- def test_iframe(self, callback_checker, stubs, js_rect):
+ def test_iframe(self, stubs, js_rect):
"""Test an element in an iframe.
0, 0 200, 0
@@ -746,32 +744,27 @@ class TestRectOnView:
assert frame.geometry().contains(iframe.geometry())
elem = get_webelem(QRect(20, 90, 10, 10), iframe,
js_rect_return=js_rect)
- elem.rect_on_view(callback=callback_checker.callback)
- callback_checker.check(QRect(20, 10 + 90, 10, 10))
+ assert elem.rect_on_view() == QRect(20, 10 + 90, 10, 10)
@pytest.mark.parametrize('js_rect', [None, {}])
- def test_passed_geometry(self, callback_checker, stubs, js_rect):
+ def test_passed_geometry(self, stubs, js_rect):
"""Make sure geometry isn't called when a geometry is passed."""
frame = stubs.FakeWebFrame(QRect(0, 0, 200, 200))
elem = get_webelem(frame=frame, js_rect_return=js_rect)
rect = QRect(10, 20, 30, 40)
- elem.rect_on_view(elem_geometry=rect,
- callback=callback_checker.callback)
- callback_checker.check(rect)
+ assert elem.rect_on_view(elem_geometry=rect) == rect
assert not elem._elem.geometry.called
@pytest.mark.parametrize('js_rect', [None, {}])
@pytest.mark.parametrize('zoom_text_only', [True, False])
- def test_zoomed(self, callback_checker, stubs, config_stub, js_rect,
- zoom_text_only):
+ def test_zoomed(self, stubs, config_stub, js_rect, zoom_text_only):
"""Make sure the coordinates are adjusted when zoomed."""
config_stub.data = {'ui': {'zoom-text-only': zoom_text_only}}
geometry = QRect(10, 10, 4, 4)
frame = stubs.FakeWebFrame(QRect(0, 0, 100, 100), zoom=0.5)
elem = get_webelem(geometry, frame, js_rect_return=js_rect,
zoom_text_only=zoom_text_only)
- elem.rect_on_view(callback=callback_checker.callback)
- callback_checker.check(QRect(10, 10, 4, 4))
+ assert elem.rect_on_view() == QRect(10, 10, 4, 4)
class TestGetChildFrames: