summaryrefslogtreecommitdiff
path: root/qutebrowser/browser/browsertab.py
diff options
context:
space:
mode:
Diffstat (limited to 'qutebrowser/browser/browsertab.py')
-rw-r--r--qutebrowser/browser/browsertab.py81
1 files changed, 72 insertions, 9 deletions
diff --git a/qutebrowser/browser/browsertab.py b/qutebrowser/browser/browsertab.py
index 699fe1b0b..e87ac806e 100644
--- a/qutebrowser/browser/browsertab.py
+++ b/qutebrowser/browser/browsertab.py
@@ -287,6 +287,61 @@ 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 is_null(self) -> bool:
+ """Whether the SearchMatch is set to zero."""
+ return self.current == 0 and 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 SearchNavigationResult(enum.Enum):
+
+ """The outcome of calling prev_/next_result."""
+
+ found = enum.auto()
+ not_found = enum.auto()
+
+ wrapped_bottom = enum.auto()
+ wrap_prevented_bottom = enum.auto()
+
+ wrapped_top = enum.auto()
+ wrap_prevented_top = enum.auto()
+
+
class AbstractSearch(QObject):
"""Attribute ``search`` of AbstractTab for doing searches.
@@ -295,17 +350,24 @@ class AbstractSearch(QObject):
text: The last thing this view was searched for.
search_displayed: Whether we're currently displaying search results in
this view.
+ 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.
+ 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.
"""
- #: Signal emitted when a search was finished
- #: (True if the text was found, False otherwise)
finished = pyqtSignal(bool)
- #: Signal emitted when an existing search was cleared.
+ match_changed = pyqtSignal(SearchMatch)
cleared = pyqtSignal()
_Callback = Callable[[bool], None]
+ _NavCallback = Callable[[SearchNavigationResult], None]
def __init__(self, tab: 'AbstractTab', parent: QWidget = None):
super().__init__(parent)
@@ -313,6 +375,7 @@ class AbstractSearch(QObject):
self._widget = cast(_WidgetType, None)
self.text: Optional[str] = None
self.search_displayed = False
+ self.match = SearchMatch()
def _is_case_sensitive(self, ignore_case: usertypes.IgnoreCase) -> bool:
"""Check if case-sensitivity should be used.
@@ -333,7 +396,6 @@ class AbstractSearch(QObject):
def search(self, text: str, *,
ignore_case: usertypes.IgnoreCase = usertypes.IgnoreCase.never,
reverse: bool = False,
- wrap: bool = True,
result_cb: _Callback = None) -> None:
"""Find the given text on the page.
@@ -341,7 +403,6 @@ class AbstractSearch(QObject):
text: The text to search for.
ignore_case: Search case-insensitively.
reverse: Reverse search direction.
- wrap: Allow wrapping at the top or bottom of the page.
result_cb: Called with a bool indicating whether a match was found.
"""
raise NotImplementedError
@@ -350,19 +411,21 @@ class AbstractSearch(QObject):
"""Clear the current search."""
raise NotImplementedError
- def prev_result(self, *, result_cb: _Callback = None) -> None:
+ def prev_result(self, *, wrap: bool = False, callback: _NavCallback = None) -> None:
"""Go to the previous result of the current search.
Args:
- result_cb: Called with a bool indicating whether a match was found.
+ wrap: Allow wrapping at the top or bottom of the page.
+ callback: Called with a SearchNavigationResult.
"""
raise NotImplementedError
- def next_result(self, *, result_cb: _Callback = None) -> None:
+ def next_result(self, *, wrap: bool = False, callback: _NavCallback = None) -> None:
"""Go to the next result of the current search.
Args:
- result_cb: Called with a bool indicating whether a match was found.
+ wrap: Allow wrapping at the top or bottom of the page.
+ callback: Called with a SearchNavigationResult.
"""
raise NotImplementedError