diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index d8bf34230..309f4e68e 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -35,6 +35,9 @@ Added - New `hints -> auto-follow-timeout` setting to ignore keypresses after following a hint when filtering in number mode. - New `:history-clear` command to clear the entire history +- New `hints -> find-implementation` to select which implementation (JS/Python) + should be used to find hints on a page. The `javascript` implementation is + better, but slower. Changed ~~~~~~~ diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index e8bde520f..e4d74f247 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -186,6 +186,7 @@ |<>|A timeout to inhibit normal-mode key bindings after a successfulauto-follow. |<>|A comma-separated list of regexes to use for 'next' links. |<>|A comma-separated list of regexes to use for 'prev' links. +|<>|Which implementation to use to find elements to hint. |============== .Quick reference for section ``colors'' @@ -1637,6 +1638,17 @@ A comma-separated list of regexes to use for 'prev' links. Default: +pass:[\bprev(ious)?\b,\bback\b,\bolder\b,\b[<←≪]\b,\b(<<|«)\b]+ +[[hints-find-implementation]] +=== find-implementation +Which implementation to use to find elements to hint. + +Valid values: + + * +javascript+: Better but slower + * +python+: Slightly worse but faster + +Default: +pass:[javascript]+ + == searchengines Definitions of search engines which can be used via the address bar. The searchengine named `DEFAULT` is used when `general -> auto-search` is true and something else than a URL was entered to be opened. Other search engines can be used by prepending the search engine name to the search term, e.g. `:open google qutebrowser`. The string `{}` will be replaced by the search term, use `{{` and `}}` for literal `{`/`}` signs. diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 3bb172300..6bdbfcf82 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -381,11 +381,12 @@ class HintManager(QObject): elem: The QWebElement to set the style attributes for. label: The label QWebElement. """ - rect = elem.rect_on_view(adjust_zoom=False) + no_js = config.get('hints', 'find-implementation') != 'javascript' + rect = elem.rect_on_view(adjust_zoom=False, no_js=no_js) left = rect.x() top = rect.y() - log.hints.vdebug("Drawing label '{!r}' at {}/{} for element '{!r}'" - .format(label, left, top, elem)) + log.hints.vdebug("Drawing label '{!r}' at {}/{} for element '{!r}' " + "(no_js: {})".format(label, left, top, elem, no_js)) label.setStyleProperty('left', '{}px !important'.format(left)) label.setStyleProperty('top', '{}px !important'.format(top)) diff --git a/qutebrowser/browser/webelem.py b/qutebrowser/browser/webelem.py index e85b0b2a5..d0679e461 100644 --- a/qutebrowser/browser/webelem.py +++ b/qutebrowser/browser/webelem.py @@ -173,14 +173,9 @@ class WebElementWrapper(collections.abc.MutableMapping): """ return is_visible(self._elem, mainframe) - def rect_on_view(self, *, adjust_zoom=True): - """Get the geometry of the element relative to the webview. - - Args: - adjust_zoom: Whether to adjust the element position based on the - current zoom level. - """ - return rect_on_view(self._elem, adjust_zoom=adjust_zoom) + def rect_on_view(self, **kwargs): + """Get the geometry of the element relative to the webview.""" + return rect_on_view(self._elem, **kwargs) def is_writable(self): """Check whether an element is writable.""" @@ -368,7 +363,7 @@ def focus_elem(frame): return WebElementWrapper(elem) -def rect_on_view(elem, *, elem_geometry=None, adjust_zoom=True): +def rect_on_view(elem, *, elem_geometry=None, adjust_zoom=True, no_js=False): """Get the geometry of the element relative to the webview. We need this as a standalone function (as opposed to a WebElementWrapper @@ -391,13 +386,14 @@ def rect_on_view(elem, *, elem_geometry=None, adjust_zoom=True): want to avoid doing it twice. adjust_zoom: Whether to adjust the element position based on the current zoom level. + no_js: Fall back to the Python implementation """ if elem.isNull(): raise IsNullError("Got called on a null element!") # First try getting the element rect via JS, as that's usually more # accurate - if elem_geometry is None: + if elem_geometry is None and not no_js: rects = elem.evaluateJavaScript("this.getClientRects()") text = utils.compact_text(elem.toOuterXml(), 500) log.hints.vdebug("Client rectangles of element '{}': {}".format(text, diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 7016bee00..094cb404c 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -930,6 +930,14 @@ def data(readonly=False): r'\b(<<|«)\b'), "A comma-separated list of regexes to use for 'prev' links."), + ('find-implementation', + SettingValue(typ.String( + valid_values=typ.ValidValues( + ('javascript', "Better but slower"), + ('python', "Slightly worse but faster"), + )), 'javascript'), + "Which implementation to use to find elements to hint."), + readonly=readonly )), diff --git a/tests/end2end/features/hints.feature b/tests/end2end/features/hints.feature index 1d5032a0d..17773c77b 100644 --- a/tests/end2end/features/hints.feature +++ b/tests/end2end/features/hints.feature @@ -232,4 +232,3 @@ Feature: Using hints And I press the key "2" And I wait for "Leaving mode KeyMode.hint (reason: all filtered)" in the log Then no crash should happen - diff --git a/tests/end2end/test_hints_html.py b/tests/end2end/test_hints_html.py index 2013f88e0..c0d7ee8cb 100644 --- a/tests/end2end/test_hints_html.py +++ b/tests/end2end/test_hints_html.py @@ -38,7 +38,9 @@ def collect_tests(): @pytest.mark.parametrize('test_name', collect_tests()) @pytest.mark.parametrize('zoom_text_only', [True, False]) @pytest.mark.parametrize('zoom_level', [100, 66, 33]) -def test_hints(test_name, zoom_text_only, zoom_level, quteproc): +@pytest.mark.parametrize('find_implementation', ['javascript', 'python']) +def test_hints(test_name, zoom_text_only, zoom_level, find_implementation, + quteproc): file_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'data', 'hints', 'html', test_name) url_path = 'data/hints/html/{}'.format(test_name) @@ -69,6 +71,7 @@ def test_hints(test_name, zoom_text_only, zoom_level, quteproc): # setup quteproc.send_cmd(':set ui zoom-text-only {}'.format(zoom_text_only)) + quteproc.set_setting('hints', 'find-implementation', find_implementation) quteproc.send_cmd(':zoom {}'.format(zoom_level)) # follow hint quteproc.send_cmd(':hint links normal') diff --git a/tests/manual/hints/find_implementation.html b/tests/manual/hints/find_implementation.html new file mode 100644 index 000000000..c51b49e5c --- /dev/null +++ b/tests/manual/hints/find_implementation.html @@ -0,0 +1,14 @@ + + + + + + Different hint implementations + + +

When setting hints -> find-implementation to python, the label for the wrapped hint should be drawn at the wrong position.

+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +
+ +