mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
E-book viewer: When searching start the search from the current position, jumping to the first match at or after the current page. Fixes #1915773 [E-book viewer: searching should select the first match from the current postion, not from the beginning of the book](https://bugs.launchpad.net/calibre/+bug/1915773)
This commit is contained in:
parent
d5cb3aa7c0
commit
6222ec8a70
@ -123,16 +123,18 @@ class SearchFinished(object):
|
||||
self.search_query = search_query
|
||||
|
||||
|
||||
class SearchResult(object):
|
||||
class SearchResult:
|
||||
|
||||
__slots__ = (
|
||||
'search_query', 'before', 'text', 'after', 'q', 'spine_idx',
|
||||
'index', 'file_name', 'is_hidden', 'offset', 'toc_nodes'
|
||||
'index', 'file_name', 'is_hidden', 'offset', 'toc_nodes',
|
||||
'result_num'
|
||||
)
|
||||
|
||||
def __init__(self, search_query, before, text, after, q, name, spine_idx, index, offset):
|
||||
def __init__(self, search_query, before, text, after, q, name, spine_idx, index, offset, result_num):
|
||||
self.search_query = search_query
|
||||
self.q = q
|
||||
self.result_num = result_num
|
||||
self.before, self.text, self.after = before, text, after
|
||||
self.spine_idx, self.index = spine_idx, index
|
||||
self.file_name = name
|
||||
@ -149,7 +151,8 @@ class SearchResult(object):
|
||||
def for_js(self):
|
||||
return {
|
||||
'file_name': self.file_name, 'spine_idx': self.spine_idx, 'index': self.index, 'text': self.text,
|
||||
'before': self.before, 'after': self.after, 'mode': self.search_query.mode, 'q': self.q
|
||||
'before': self.before, 'after': self.after, 'mode': self.search_query.mode, 'q': self.q,
|
||||
'result_num': self.result_num
|
||||
}
|
||||
|
||||
def is_result(self, result_from_js):
|
||||
@ -514,7 +517,6 @@ class Results(QTreeWidget): # {{{
|
||||
self.search_results.append(result)
|
||||
n = self.number_of_results
|
||||
self.count_changed.emit(n)
|
||||
return n
|
||||
|
||||
def item_activated(self):
|
||||
i = self.currentItem()
|
||||
@ -545,6 +547,14 @@ class Results(QTreeWidget): # {{{
|
||||
item.setIcon(0, self.not_found_icon)
|
||||
break
|
||||
|
||||
def search_result_discovered(self, sr):
|
||||
q = sr['result_num']
|
||||
for i in range(self.number_of_results):
|
||||
item = self.item_map[i]
|
||||
r = item.data(0, SEARCH_RESULT_ROLE)
|
||||
if r.result_num == q:
|
||||
self.setCurrentItem(item)
|
||||
|
||||
@property
|
||||
def current_result_is_hidden(self):
|
||||
item = self.currentItem()
|
||||
@ -588,6 +598,7 @@ class SearchPanel(QWidget): # {{{
|
||||
|
||||
def __init__(self, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
self.discovery_counter = 0
|
||||
self.last_hidden_text_warning = None
|
||||
self.current_search = None
|
||||
self.anchor_cfi = None
|
||||
@ -643,6 +654,7 @@ class SearchPanel(QWidget): # {{{
|
||||
self.current_search = search_query
|
||||
self.last_hidden_text_warning = None
|
||||
self.search_tasks.put((search_query, current_name))
|
||||
self.discovery_counter += 1
|
||||
|
||||
def set_anchor_cfi(self, pos_data):
|
||||
self.anchor_cfi = pos_data['cfi']
|
||||
@ -666,6 +678,7 @@ class SearchPanel(QWidget): # {{{
|
||||
self.results_found.emit(SearchFinished(search_query))
|
||||
continue
|
||||
num_in_spine = len(spine)
|
||||
result_num = 0
|
||||
for n in range(num_in_spine):
|
||||
idx = (spine_idx + n) % num_in_spine
|
||||
name = spine[idx]
|
||||
@ -674,7 +687,8 @@ class SearchPanel(QWidget): # {{{
|
||||
for i, result in enumerate(search_in_name(name, search_query)):
|
||||
before, text, after, offset = result
|
||||
q = (before or '')[-5:] + text + (after or '')[:5]
|
||||
self.results_found.emit(SearchResult(search_query, before, text, after, q, name, idx, counter[q], offset))
|
||||
result_num += 1
|
||||
self.results_found.emit(SearchResult(search_query, before, text, after, q, name, idx, counter[q], offset, result_num))
|
||||
counter[q] += 1
|
||||
except Exception:
|
||||
import traceback
|
||||
@ -691,10 +705,10 @@ class SearchPanel(QWidget): # {{{
|
||||
else:
|
||||
self.show_no_results_found()
|
||||
return
|
||||
if self.results.add_result(result) == 1:
|
||||
# first result
|
||||
self.results.select_first_result()
|
||||
self.results.item_activated()
|
||||
self.results.add_result(result)
|
||||
obj = result.for_js
|
||||
obj['on_discovery'] = self.discovery_counter
|
||||
self.show_search_result.emit(obj)
|
||||
self.update_hidden_message()
|
||||
|
||||
def visibility_changed(self, visible):
|
||||
@ -730,6 +744,9 @@ class SearchPanel(QWidget): # {{{
|
||||
self.results.search_result_not_found(sr)
|
||||
self.update_hidden_message()
|
||||
|
||||
def search_result_discovered(self, sr):
|
||||
self.results.search_result_discovered(sr)
|
||||
|
||||
def show_no_results_found(self):
|
||||
msg = _('No matches were found for:')
|
||||
warning_dialog(self, _('No matches found'), msg + ' <b>{}</b>'.format(self.current_search.text), show=True)
|
||||
|
@ -168,6 +168,7 @@ class EbookViewer(MainWindow):
|
||||
self.web_view.find_next.connect(self.search_widget.find_next_requested)
|
||||
self.search_widget.show_search_result.connect(self.web_view.show_search_result)
|
||||
self.web_view.search_result_not_found.connect(self.search_widget.search_result_not_found)
|
||||
self.web_view.search_result_discovered.connect(self.search_widget.search_result_discovered)
|
||||
self.web_view.toggle_bookmarks.connect(self.toggle_bookmarks)
|
||||
self.web_view.toggle_highlights.connect(self.toggle_highlights)
|
||||
self.web_view.new_bookmark.connect(self.bookmarks_widget.create_new_bookmark)
|
||||
|
@ -248,6 +248,7 @@ class ViewerBridge(Bridge):
|
||||
toggle_lookup = from_js(object)
|
||||
show_search = from_js(object, object)
|
||||
search_result_not_found = from_js(object)
|
||||
search_result_discovered = from_js(object)
|
||||
find_next = from_js(object)
|
||||
quit = from_js()
|
||||
update_current_toc_nodes = from_js(object)
|
||||
@ -449,6 +450,7 @@ class WebView(RestartingWebEngineView):
|
||||
toggle_toc = pyqtSignal()
|
||||
show_search = pyqtSignal(object, object)
|
||||
search_result_not_found = pyqtSignal(object)
|
||||
search_result_discovered = pyqtSignal(object)
|
||||
find_next = pyqtSignal(object)
|
||||
toggle_bookmarks = pyqtSignal()
|
||||
toggle_highlights = pyqtSignal()
|
||||
@ -507,6 +509,7 @@ class WebView(RestartingWebEngineView):
|
||||
self.bridge.toggle_toc.connect(self.toggle_toc)
|
||||
self.bridge.show_search.connect(self.show_search)
|
||||
self.bridge.search_result_not_found.connect(self.search_result_not_found)
|
||||
self.bridge.search_result_discovered.connect(self.search_result_discovered)
|
||||
self.bridge.find_next.connect(self.find_next)
|
||||
self.bridge.toggle_bookmarks.connect(self.toggle_bookmarks)
|
||||
self.bridge.toggle_highlights.connect(self.toggle_highlights)
|
||||
|
@ -110,6 +110,17 @@ class EPUBReadingSystem:
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
class FullBookSearch:
|
||||
|
||||
def __init__(self):
|
||||
start_spine_index = current_spine_item()?.index
|
||||
if not start_spine_index?:
|
||||
start_spine_index = -1
|
||||
self.start_spine_index = start_spine_index
|
||||
self.progress_frac_at_start = progress_frac()
|
||||
self.first_result_shown = False
|
||||
|
||||
|
||||
class IframeBoss:
|
||||
|
||||
def __init__(self):
|
||||
@ -122,6 +133,7 @@ class IframeBoss:
|
||||
self.content_ready = False
|
||||
self.last_window_width = self.last_window_height = -1
|
||||
self.forward_keypresses = False
|
||||
self.full_book_search_in_progress = None
|
||||
set_boss(self)
|
||||
handlers = {
|
||||
'change_color_scheme': self.change_color_scheme,
|
||||
@ -746,9 +758,24 @@ class IframeBoss:
|
||||
self.send_message(msg_type, text=data.text, backwards=data.backwards, searched_in_spine=data.searched_in_spine)
|
||||
|
||||
def show_search_result(self, data, from_load):
|
||||
sr = data.search_result
|
||||
if sr.on_discovery:
|
||||
if sr.result_num is 1:
|
||||
self.full_book_search_in_progress = FullBookSearch()
|
||||
elif self.full_book_search_in_progress?.first_result_shown:
|
||||
return
|
||||
self.last_search_at = window.performance.now()
|
||||
if select_search_result(data.search_result):
|
||||
x, y = scroll_viewport.x(), scroll_viewport.y()
|
||||
if select_search_result(sr):
|
||||
self.ensure_selection_visible()
|
||||
if self.full_book_search_in_progress and not self.full_book_search_in_progress.first_result_shown and sr.on_discovery:
|
||||
discovered = False
|
||||
if progress_frac() >= self.full_book_search_in_progress.progress_frac_at_start or current_spine_item().index is not self.full_book_search_in_progress.start_spine_index:
|
||||
self.full_book_search_in_progress.first_result_shown = True
|
||||
discovered = True
|
||||
else:
|
||||
scroll_viewport.scroll_to(x, y)
|
||||
self.send_message('search_result_discovered', search_result=data.search_result, discovered=discovered)
|
||||
else:
|
||||
self.send_message('search_result_not_found', search_result=data.search_result)
|
||||
|
||||
|
@ -287,10 +287,8 @@ class View:
|
||||
'update_cfi': self.on_update_cfi,
|
||||
'update_progress_frac': self.on_update_progress_frac,
|
||||
'update_toc_position': self.on_update_toc_position,
|
||||
'search_result_not_found': def (data):
|
||||
if ui_operations.search_result_not_found:
|
||||
ui_operations.search_result_not_found(data.search_result)
|
||||
,
|
||||
'search_result_not_found': self.search_result_not_found,
|
||||
'search_result_discovered': self.search_result_discovered,
|
||||
'annotations': self.on_annotations_message,
|
||||
'tts': self.on_tts_message,
|
||||
'hints': self.on_hints_message,
|
||||
@ -1374,11 +1372,39 @@ class View:
|
||||
if ui_operations.reference_mode_changed:
|
||||
ui_operations.reference_mode_changed(self.reference_mode_enabled)
|
||||
|
||||
def discover_search_result(self, sr):
|
||||
if sr.result_num is 1:
|
||||
self.search_result_discovery = {'queue': v'[]', 'on_discovery': sr.on_discovery, 'in_flight': None, 'discovered': False}
|
||||
if not self.search_result_discovery or self.search_result_discovery.discovered or self.search_result_discovery.on_discovery is not sr.on_discovery:
|
||||
return
|
||||
self.search_result_discovery.queue.push(sr)
|
||||
if not self.search_result_discovery.in_flight:
|
||||
self.show_search_result(self.search_result_discovery.queue.shift())
|
||||
|
||||
def handle_search_result_discovery(self, sr, discovered):
|
||||
if self.search_result_discovery?.on_discovery is sr.on_discovery:
|
||||
self.search_result_discovery.in_flight = None
|
||||
if discovered:
|
||||
self.search_result_discovery.discovered = True
|
||||
ui_operations.search_result_discovered(sr)
|
||||
elif not self.search_result_discovery.discovered and self.search_result_discovery.queue.length:
|
||||
self.show_search_result(self.search_result_discovery.queue.shift())
|
||||
|
||||
def search_result_discovered(self, data):
|
||||
self.handle_search_result_discovery(data.search_result, data.discovered)
|
||||
|
||||
def search_result_not_found(self, data):
|
||||
if ui_operations.search_result_not_found:
|
||||
ui_operations.search_result_not_found(data.search_result)
|
||||
self.handle_search_result_discovery(data.search_result, False)
|
||||
|
||||
def show_search_result(self, sr):
|
||||
if self.currently_showing.name is sr.file_name:
|
||||
self.iframe_wrapper.send_message('show_search_result', search_result=sr)
|
||||
else:
|
||||
self.show_name(sr.file_name, initial_position={'type':'search_result', 'search_result':sr, 'replace_history':True})
|
||||
if self.search_result_discovery?.on_discovery is sr.on_discovery:
|
||||
self.search_result_discovery.in_flight = sr.result_num
|
||||
|
||||
def highlight_action(self, uuid, which):
|
||||
spine = self.book.manifest.spine
|
||||
|
@ -270,7 +270,10 @@ def trigger_shortcut(which):
|
||||
@from_python
|
||||
def show_search_result(sr):
|
||||
if view:
|
||||
view.show_search_result(sr)
|
||||
if sr.on_discovery:
|
||||
view.discover_search_result(sr)
|
||||
else:
|
||||
view.show_search_result(sr)
|
||||
|
||||
@from_python
|
||||
def prepare_for_close():
|
||||
@ -403,6 +406,8 @@ if window is window.top:
|
||||
to_python.read_aloud_state_changed(active)
|
||||
ui_operations.search_result_not_found = def(sr):
|
||||
to_python.search_result_not_found(sr)
|
||||
ui_operations.search_result_discovered = def(sr):
|
||||
to_python.search_result_discovered(sr)
|
||||
ui_operations.scrollbar_context_menu = def(x, y, frac):
|
||||
to_python.scrollbar_context_menu(x, y, frac)
|
||||
ui_operations.close_prep_finished = def(cfi):
|
||||
|
Loading…
x
Reference in New Issue
Block a user