diff options
author | Florian Bruhin <me@the-compiler.org> | 2022-06-13 16:18:42 +0200 |
---|---|---|
committer | Florian Bruhin <me@the-compiler.org> | 2022-06-13 18:05:37 +0200 |
commit | 265b018c172f8c1f6d9e7f8850256363f0629f82 (patch) | |
tree | 753c3cafebe47edf1338a953cb85f7a72f6f6cd4 | |
parent | 583354d524ff42cfb24ff5618d3f8b1919e9d554 (diff) | |
download | qutebrowser-265b018c172f8c1f6d9e7f8850256363f0629f82.tar.gz qutebrowser-265b018c172f8c1f6d9e7f8850256363f0629f82.zip |
Add a SearchMatch helper class
-rw-r--r-- | qutebrowser/browser/browsertab.py | 53 | ||||
-rw-r--r-- | qutebrowser/browser/commands.py | 24 | ||||
-rw-r--r-- | qutebrowser/browser/webengine/webenginetab.py | 52 | ||||
-rw-r--r-- | qutebrowser/mainwindow/mainwindow.py | 2 | ||||
-rw-r--r-- | qutebrowser/mainwindow/statusbar/searchmatch.py | 17 | ||||
-rw-r--r-- | qutebrowser/mainwindow/tabbedbrowser.py | 7 |
6 files changed, 85 insertions, 70 deletions
diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py index f8ac3d24c..7745cff4b 100644 --- a/qutebrowser/browser/browsertab.py +++ b/qutebrowser/browser/browsertab.py @@ -287,6 +287,43 @@ class AbstractPrinting: diag.open(do_print) +@dataclasses.dataclass +class SearchMatch: + + """The currently highlighted search match. + + Attributes: + current: The currently active search match on the page. + 0 if no search is active or the feature isn't available. + total: The total number of search matches on the page. + 0 if no search is active or the feature isn't available. + """ + + current: int = 0 + total: int = 0 + + def reset(self) -> None: + """Reset match counter information. + + Stale information could lead to next_result or prev_result misbehaving. + """ + self.current = 0 + self.total = 0 + + def at_limit(self, going_up: bool) -> bool: + """Whether the SearchMatch is currently at the first/last result.""" + return ( + self.total != 0 and + ( + going_up and self.current == 1 or + not going_up and self.current == self.total + ) + ) + + def __str__(self) -> str: + return f"{self.current}/{self.total}" + + class AbstractSearch(QObject): """Attribute ``search`` of AbstractTab for doing searches. @@ -295,23 +332,20 @@ class AbstractSearch(QObject): text: The last thing this view was searched for. search_displayed: Whether we're currently displaying search results in this view. - current_match: The currently active search match on the page. - 0 if no search is active or the feature isn't available. - total_match_count: The total number of search matches on the page. - 0 if no search is active or the feature isn't available. + match: The currently active search match. _flags: The flags of the last search (needs to be set by subclasses). _widget: The underlying WebView widget. Signals: finished: A search has finished. True if the text was found, false otherwise. - search_match_changed: The currently active search match has changed. - Emits (0, 0) if no search is active. - Will not be emitted if search matches are not available. + match_changed: The currently active search match has changed. + Emits SearchMatch(0, 0) if no search is active. + Will not be emitted if search matches are not available. cleared: An existing search was cleared. """ finished = pyqtSignal(bool) - search_match_changed = pyqtSignal(int, int) + match_changed = pyqtSignal(SearchMatch) cleared = pyqtSignal() _Callback = Callable[[bool], None] @@ -322,8 +356,7 @@ class AbstractSearch(QObject): self._widget = cast(_WidgetType, None) self.text: Optional[str] = None self.search_displayed = False - self.current_match = 0 - self.total_match_count = 0 + self.match = SearchMatch() def _is_case_sensitive(self, ignore_case: usertypes.IgnoreCase) -> bool: """Check if case-sensitivity should be used. diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 939f02108..50dae13ea 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1538,14 +1538,14 @@ class CommandDispatcher: message.error(str(e)) ed.backup() - def _search_cb(self, found, *, tab, old_current_match, options, text, prev): + def _search_cb(self, found, *, tab, old_match, options, text, prev): """Callback called from search/search_next/search_prev. Args: found: Whether the text was found. tab: The AbstractTab in which the search was made. - old_current_match: The previously active match before the search - was performed. + old_match: The previously active search match before the search was + performed. options: The options (dict) the search was made with. text: The text searched for. prev: Whether we're searching backwards (i.e. :search-prev) @@ -1560,20 +1560,19 @@ class CommandDispatcher: if not config.val.search.wrap_messages: return - new_current_match = tab.search.current_match # Check if the match count change is opposite to the search direction - if old_current_match > 0: + if old_match.current > 0: if not going_up: - if old_current_match > new_current_match: + if old_match.current > tab.search.match.current: message.info("Search hit BOTTOM, continuing at TOP", replace="search-hit-msg") - elif old_current_match == new_current_match: + elif old_match.current == tab.search.match.current: message.info("Search hit BOTTOM", replace="search-hit-msg") elif going_up: - if old_current_match < new_current_match: + if old_match.current < tab.search.match.current: message.info("Search hit TOP, continuing at BOTTOM", replace="search-hit-msg") - elif old_current_match == new_current_match: + elif old_match.current == tab.search.match.current: message.info("Search hit TOP", replace="search-hit-msg") else: message.warning(f"Text '{text}' not found on page!", @@ -1605,7 +1604,8 @@ class CommandDispatcher: self._tabbed_browser.search_options = dict(options) cb = functools.partial(self._search_cb, tab=tab, - old_current_match=0, options=options, + old_match=browsertab.SearchMatch(), + options=options, text=text, prev=False) options['result_cb'] = cb @@ -1638,7 +1638,7 @@ class CommandDispatcher: return cb = functools.partial(self._search_cb, tab=tab, - old_current_match=tab.search.current_match, + old_match=tab.search.match, options=window_options, text=window_text, prev=False) @@ -1672,7 +1672,7 @@ class CommandDispatcher: return cb = functools.partial(self._search_cb, tab=tab, - old_current_match=tab.search.current_match, + old_match=tab.search.match, options=window_options, text=window_text, prev=True) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 1d02f7e99..e513e4e24 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -112,25 +112,21 @@ class _WebEngineSearchWrapHandler: self.flag_wrap = True self.nowrap_available = False - def prevent_wrapping(self, current_match, total_match_count, *, going_up): + def prevent_wrapping( + self, + match: browsertab.SearchMatch, + *, + going_up: bool, + ) -> None: """Prevent wrapping if possible and required. Returns True if a wrap was prevented and False if not. Args: - current_match: The currently active search match on the page. - total_match_count: The total number of search matches on the page. + match: The currently active search match on the page. going_up: Whether the search would scroll the page up or down. """ - return ( - self.nowrap_available and - not self.flag_wrap and - total_match_count != 0 and - ( - going_up and current_match == 1 or - not going_up and current_match == total_match_count - ) - ) + return self.nowrap_available and not self.flag_wrap and match.at_limit(going_up) class WebEngineSearch(browsertab.AbstractSearch): @@ -224,11 +220,10 @@ class WebEngineSearch(browsertab.AbstractSearch): def _on_find_finished(self, find_text_result): """Unwrap the result, store it, and pass it along.""" - self.current_match = find_text_result.activeMatch() - self.total_match_count = find_text_result.numberOfMatches() - log.webview.debug("Active search match: {}/{}" - .format(self.current_match, self.total_match_count)) - self.search_match_changed.emit(self.current_match, self.total_match_count) + self.match.current = find_text_result.activeMatch() + self.match.total = find_text_result.numberOfMatches() + log.webview.debug(f"Active search match: {self.match}") + self.match_changed.emit(self.match) def search(self, text, *, ignore_case=usertypes.IgnoreCase.never, reverse=False, wrap=True, result_cb=None): @@ -241,7 +236,7 @@ class WebEngineSearch(browsertab.AbstractSearch): self.text = text self._flags = self._args_to_flags(reverse, ignore_case) - self._reset_match_data() + self.match.reset() self._wrap_handler.flag_wrap = wrap self._find(text, self._flags, result_cb, 'search') @@ -249,23 +244,21 @@ class WebEngineSearch(browsertab.AbstractSearch): def clear(self): if self.search_displayed: self.cleared.emit() - self.search_match_changed.emit(0, 0) + self.match_changed.emit(browsertab.SearchMatch()) self.search_displayed = False - self._reset_match_data() + self.match.reset() self._widget.page().findText('') def prev_result(self, *, result_cb=None): # The int() here makes sure we get a copy of the flags. flags = QWebEnginePage.FindFlags(int(self._flags)) if flags & QWebEnginePage.FindBackward: - if self._wrap_handler.prevent_wrapping(self.current_match, - self.total_match_count, going_up=False): + if self._wrap_handler.prevent_wrapping(self.match, going_up=False): result_cb(True) return flags &= ~QWebEnginePage.FindBackward else: - if self._wrap_handler.prevent_wrapping(self.current_match, - self.total_match_count, going_up=True): + if self._wrap_handler.prevent_wrapping(self.match, going_up=True): result_cb(True) return flags |= QWebEnginePage.FindBackward @@ -273,20 +266,11 @@ class WebEngineSearch(browsertab.AbstractSearch): def next_result(self, *, result_cb=None): going_up = self._flags & QWebEnginePage.FindBackward - if self._wrap_handler.prevent_wrapping(self.current_match, - self.total_match_count, going_up=going_up): + if self._wrap_handler.prevent_wrapping(self.match, going_up=going_up): result_cb(True) return self._find(self.text, self._flags, result_cb, 'next_result') - def _reset_match_data(self): - """Reset match counter information. - - Stale information could lead to next_result or prev_result misbehaving. - """ - self.current_match = 0 - self.total_match_count = 0 - class WebEngineCaret(browsertab.AbstractCaret): diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py index c9744cfc1..22245d8c1 100644 --- a/qutebrowser/mainwindow/mainwindow.py +++ b/qutebrowser/mainwindow/mainwindow.py @@ -538,7 +538,7 @@ class MainWindow(QWidget): self.status.url.on_load_status_changed) self.tabbed_browser.cur_search_match_changed.connect( - self.status.search_match.set_match_index) + self.status.search_match.set_match) self.tabbed_browser.cur_caret_selection_toggled.connect( self.status.on_caret_selection_toggled) diff --git a/qutebrowser/mainwindow/statusbar/searchmatch.py b/qutebrowser/mainwindow/statusbar/searchmatch.py index 6e9a435de..da337b19a 100644 --- a/qutebrowser/mainwindow/statusbar/searchmatch.py +++ b/qutebrowser/mainwindow/statusbar/searchmatch.py @@ -23,6 +23,7 @@ from PyQt5.QtCore import pyqtSlot from qutebrowser.utils import log +from qutebrowser.browser import browsertab from qutebrowser.mainwindow.statusbar import textbase @@ -30,20 +31,18 @@ class SearchMatch(textbase.TextBase): """The part of the statusbar that displays the search match counter.""" - @pyqtSlot(int, int) - def set_match_index(self, current: int, total: int) -> None: + @pyqtSlot(browsertab.SearchMatch) + def set_match(self, match: browsertab.SearchMatch) -> None: """Set the match counts in the statusbar. - Passing (0, 0) hides the match counter. + Passing SearchMatch(0, 0) hides the match counter. Args: - current: The currently active search match. - total: The total number of search matches on the page. + match: The currently active search match. """ - if current <= 0 and total <= 0: + if match.current <= 0 and match.total <= 0: self.setText('') log.statusbar.debug('Clearing search match text.') else: - self.setText('Match [{}/{}]'.format(current, total)) - log.statusbar.debug('Setting search match text to {}/{}' - .format(current, total)) + self.setText(f'Match [{match}]') + log.statusbar.debug(f'Setting search match text to {match}') diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 9a98b3d70..c623ce809 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -202,7 +202,7 @@ class TabbedBrowser(QWidget): cur_link_hovered = pyqtSignal(str) cur_scroll_perc_changed = pyqtSignal(int, int) cur_load_status_changed = pyqtSignal(usertypes.LoadStatus) - cur_search_match_changed = pyqtSignal(int, int) + cur_search_match_changed = pyqtSignal(browsertab.SearchMatch) cur_fullscreen_requested = pyqtSignal(bool) cur_caret_selection_toggled = pyqtSignal(browsertab.SelectionState) close_window = pyqtSignal() @@ -349,7 +349,7 @@ class TabbedBrowser(QWidget): self._filter.create(self.cur_fullscreen_requested, tab)) tab.caret.selection_toggled.connect( self._filter.create(self.cur_caret_selection_toggled, tab)) - tab.search.search_match_changed.connect( + tab.search.match_changed.connect( self._filter.create(self.cur_search_match_changed, tab)) # misc tab.scroller.perc_changed.connect(self._on_scroll_pos_changed) @@ -905,8 +905,7 @@ class TabbedBrowser(QWidget): .format(current_mode.name, mode_on_change)) self._now_focused = tab self.current_tab_changed.emit(tab) - self.cur_search_match_changed.emit(tab.search.current_match, - tab.search.total_match_count) + self.cur_search_match_changed.emit(tab.search.match) QTimer.singleShot(0, self._update_window_title) self._tab_insert_idx_left = self.widget.currentIndex() self._tab_insert_idx_right = self.widget.currentIndex() + 1 |