From 0b88c5d413fafcfa47e1e55236c637765337d5ec Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 4 Jul 2016 11:23:46 +0200 Subject: [PATCH] Re-implement searching for QtWebKit --- qutebrowser/browser/commands.py | 89 +++++++------------ qutebrowser/browser/tab.py | 42 +++++++++ qutebrowser/browser/webengine/webenginetab.py | 8 ++ qutebrowser/browser/webkit/webkittab.py | 41 +++++++++ qutebrowser/browser/webkit/webview.py | 4 - qutebrowser/mainwindow/tabbedbrowser.py | 6 +- 6 files changed, 126 insertions(+), 64 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 5cd670df1..ce18321c7 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1400,17 +1400,6 @@ class CommandDispatcher: this.dispatchEvent(event); """.format(webelem.javascript_escape(sel))) - def _clear_search(self, view, text): - """Clear search string/highlights for the given view. - - This does nothing if the view's search text is the same as the given - text. - """ - if view.search_text is not None and view.search_text != text: - # We first clear the marked text, then the highlights - view.search('', 0) - view.search('', QWebPage.HighlightAllOccurrences) - @cmdutils.register(instance='command-dispatcher', scope='window', maxsplit=0) def search(self, text="", reverse=False): @@ -1421,27 +1410,18 @@ class CommandDispatcher: reverse: Reverse search direction. """ self.set_mark("'") - view = self._current_widget() - self._clear_search(view, text) - flags = 0 - ignore_case = config.get('general', 'ignore-case') - if ignore_case == 'smart': - if not text.islower(): - flags |= QWebPage.FindCaseSensitively - elif not ignore_case: - flags |= QWebPage.FindCaseSensitively - if config.get('general', 'wrap-search'): - flags |= QWebPage.FindWrapsAroundDocument - if reverse: - flags |= QWebPage.FindBackward - # We actually search *twice* - once to highlight everything, then again - # to get a mark so we can navigate. - view.search(text, flags) - view.search(text, flags | QWebPage.HighlightAllOccurrences) - view.search_text = text - view.search_flags = flags + tab = self._current_widget() + tab.search.clear() + + options = { + 'ignore_case': config.get('general', 'ignore-case'), + 'wrap': config.get('general', 'wrap-search'), + 'reverse': reverse, + } + tab.search.search(text, **options) + self._tabbed_browser.search_text = text - self._tabbed_browser.search_flags = flags + self._tabbed_browser.search_options = options @cmdutils.register(instance='command-dispatcher', hide=True, scope='window') @@ -1452,18 +1432,19 @@ class CommandDispatcher: Args: count: How many elements to ignore. """ + tab = self._current_widget() + window_text = self._tabbed_browser.search_text + window_options = self._tabbed_browser.search_options + self.set_mark("'") - view = self._current_widget() - self._clear_search(view, self._tabbed_browser.search_text) + if window_text is not None and window_text != tab.search.text: + tab.search.clear() + tab.search.search(window_text, **window_options) + count -= 1 - if self._tabbed_browser.search_text is not None: - view.search_text = self._tabbed_browser.search_text - view.search_flags = self._tabbed_browser.search_flags - view.search(view.search_text, - view.search_flags | QWebPage.HighlightAllOccurrences) - for _ in range(count): - view.search(view.search_text, view.search_flags) + for _ in range(count): + tab.search.next_result() @cmdutils.register(instance='command-dispatcher', hide=True, scope='window') @@ -1474,25 +1455,19 @@ class CommandDispatcher: Args: count: How many elements to ignore. """ - self.set_mark("'") - view = self._current_widget() - self._clear_search(view, self._tabbed_browser.search_text) + tab = self._current_widget() + window_text = self._tabbed_browser.search_text + window_options = self._tabbed_browser.search_options + + self.set_mark("'") + + if window_text is not None and window_text != tab.search.text: + tab.search.clear() + tab.search.search(window_text, **window_options) + count -= 1 - if self._tabbed_browser.search_text is not None: - view.search_text = self._tabbed_browser.search_text - view.search_flags = self._tabbed_browser.search_flags - view.search(view.search_text, - view.search_flags | QWebPage.HighlightAllOccurrences) - # The int() here serves as a QFlags constructor to create a copy of the - # QFlags instance rather as a reference. I don't know why it works this - # way, but it does. - flags = int(view.search_flags) - if flags & QWebPage.FindBackward: - flags &= ~QWebPage.FindBackward - else: - flags |= QWebPage.FindBackward for _ in range(count): - view.search(view.search_text, flags) + tab.search.prev_result() @cmdutils.register(instance='command-dispatcher', hide=True, modes=[KeyMode.caret], scope='window') diff --git a/qutebrowser/browser/tab.py b/qutebrowser/browser/tab.py index 98d5ad7df..f54a0d742 100644 --- a/qutebrowser/browser/tab.py +++ b/qutebrowser/browser/tab.py @@ -56,6 +56,46 @@ class WrapperLayout(QLayout): self._widget.setGeometry(r) +class AbstractSearch(QObject): + + """Attribute of AbstractTab for doing searches. + + Attributes: + widget: The underlying WebView widget. + text: The last thing this view was searched for. + _flags: The flags of the last search. + """ + + def __init__(self, parent=None): + super().__init__(parent) + self.widget = None + self.text = None + self._flags = 0 + + def search(self, text, *, ignore_case=False, wrap=False): + """Find the given text on the page. + + Args: + text: The text to search for. + ignore_case: Search case-insensitively. (True/False/'smart') + wrap: Wrap around to the top when arriving at the bottom. + reverse: Reverse search direction. + """ + raise NotImplementedError + + def clear(self): + """Clear the current search.""" + raise NotImplementedError + + def prev_result(self): + """Go to the previous result of the current search.""" + raise NotImplementedError + + def next_result(self): + """Go to the next result of the current search.""" + raise NotImplementedError + + class AbstractZoom(QObject): """Attribute of AbstractTab for controlling zoom. @@ -357,6 +397,7 @@ class AbstractTab(QWidget): # self.scroll = AbstractScroller(parent=self) # self.caret = AbstractCaret(win_id=win_id, parent=self) # self.zoom = AbstractZoom(win_id=win_id) + # self.search = AbstractSearch(parent=self) self._layout = None self._widget = None self.keep_icon = False # FIXME:refactor get rid of this? @@ -368,6 +409,7 @@ class AbstractTab(QWidget): self.scroll.widget = widget self.caret.widget = widget self.zoom.widget = widget + self.search.widget = widget widget.mouse_wheel_zoom.connect(self.zoom.on_mouse_wheel_zoom) widget.setParent(self) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 8fb8018f7..9f46bec9f 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -31,6 +31,13 @@ from qutebrowser.browser import tab from qutebrowser.utils import usertypes, qtutils +class WebEngineSearch(tab.AbstractSearch): + + ## TODO + + pass + + class WebEngineCaret(tab.AbstractCaret): ## TODO @@ -100,6 +107,7 @@ class WebEngineViewTab(tab.AbstractTab): self.scroll = WebEngineScroller() self.caret = WebEngineCaret(win_id=win_id, parent=self) self.zoom = WebEngineZoom(win_id=win_id, parent=self) + self.search = WebEngineSearch(parent=self) self._set_widget(widget) self._connect_signals() diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index c9a555a27..e6e7523e0 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -31,6 +31,46 @@ from qutebrowser.browser.webkit import webview from qutebrowser.utils import qtutils, objreg, usertypes, utils +class WebViewSearch(tab.AbstractSearch): + + def clear(self): + # We first clear the marked text, then the highlights + self.widget.search('', 0) + self.widget.search('', QWebPage.HighlightAllOccurrences) + + def search(self, text, *, ignore_case=False, wrap=False, reverse=False): + flags = 0 + if ignore_case == 'smart': + if not text.islower(): + flags |= QWebPage.FindCaseSensitively + elif not ignore_case: + flags |= QWebPage.FindCaseSensitively + if wrap: + flags |= QWebPage.FindWrapsAroundDocument + if reverse: + flags |= QWebPage.FindBackward + # We actually search *twice* - once to highlight everything, then again + # to get a mark so we can navigate. + self.widget.search(text, flags) + self.widget.search(text, flags | QWebPage.HighlightAllOccurrences) + self.text = text + self._flags = flags + + def next_result(self): + self.widget.search(self.text, self._flags) + + def prev_result(self): + # The int() here serves as a QFlags constructor to create a copy of the + # QFlags instance rather as a reference. I don't know why it works this + # way, but it does. + flags = int(self._flags) + if flags & QWebPage.FindBackward: + flags &= ~QWebPage.FindBackward + else: + flags |= QWebPage.FindBackward + self.widget.search(self.text, flags) + + class WebViewCaret(tab.AbstractCaret): @pyqtSlot(usertypes.KeyMode) @@ -376,6 +416,7 @@ class WebViewTab(tab.AbstractTab): self.scroll = WebViewScroller(parent=self) self.caret = WebViewCaret(win_id=win_id, parent=self) self.zoom = WebViewZoom(win_id=win_id, parent=self) + self.search = WebViewSearch(parent=self) self._set_widget(widget) self._connect_signals() self.zoom._set_default_zoom() diff --git a/qutebrowser/browser/webkit/webview.py b/qutebrowser/browser/webkit/webview.py index bd6fffcd3..b16c1be19 100644 --- a/qutebrowser/browser/webkit/webview.py +++ b/qutebrowser/browser/webkit/webview.py @@ -54,8 +54,6 @@ class WebView(QWebView): code. registry: The ObjectRegistry associated with this tab. win_id: The window ID of the view. - search_text: The text of the last search. - 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. _old_scroll_pos: The old scroll position. @@ -101,8 +99,6 @@ class WebView(QWebView): self._has_ssl_errors = False self._ignore_wheel_event = False self.keep_icon = False - self.search_text = None - self.search_flags = 0 self._set_bg_color() self.cur_url = QUrl() self.progress = 0 diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 41085adcf..b8e6024f4 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -57,8 +57,8 @@ class TabbedBrowser(tabwidget.TabWidget): emitted if the signal occurred in the current tab. Attributes: - search_text/search_flags: Search parameters which are shared between - all tabs. + search_text/search_options: Search parameters which are shared between + all tabs. _win_id: The window ID this tabbedbrowser is associated with. _filter: A SignalFilter instance. _now_focused: The tab which is focused now. @@ -118,7 +118,7 @@ class TabbedBrowser(tabwidget.TabWidget): self._filter = signalfilter.SignalFilter(win_id, self) self._now_focused = None self.search_text = None - self.search_flags = 0 + self.search_options = {} self._local_marks = {} self._global_marks = {} self.default_window_icon = self.window().windowIcon()