mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Viewer: Ensure last read position is fully accurate
This is particularly important when quitting while autoscroll is active as CFI is not updated during autoscroll.
This commit is contained in:
parent
9730226f6e
commit
2be73652a6
@ -14,7 +14,7 @@ from threading import Thread
|
|||||||
|
|
||||||
from PyQt5.Qt import (
|
from PyQt5.Qt import (
|
||||||
QApplication, QCursor, QDockWidget, QEvent, QMenu, QMimeData, QModelIndex,
|
QApplication, QCursor, QDockWidget, QEvent, QMenu, QMimeData, QModelIndex,
|
||||||
QPixmap, Qt, QToolBar, QUrl, QVBoxLayout, QWidget, pyqtSignal
|
QPixmap, Qt, QTimer, QToolBar, QUrl, QVBoxLayout, QWidget, pyqtSignal
|
||||||
)
|
)
|
||||||
|
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
@ -87,7 +87,7 @@ class EbookViewer(MainWindow):
|
|||||||
|
|
||||||
def __init__(self, open_at=None, continue_reading=None, force_reload=False):
|
def __init__(self, open_at=None, continue_reading=None, force_reload=False):
|
||||||
MainWindow.__init__(self, None)
|
MainWindow.__init__(self, None)
|
||||||
self.shutting_down = False
|
self.shutting_down = self.close_forced = False
|
||||||
self.force_reload = force_reload
|
self.force_reload = force_reload
|
||||||
connect_lambda(self.book_preparation_started, self, lambda self: self.loading_overlay(_(
|
connect_lambda(self.book_preparation_started, self, lambda self: self.loading_overlay(_(
|
||||||
'Preparing book for first read, please wait')), type=Qt.QueuedConnection)
|
'Preparing book for first read, please wait')), type=Qt.QueuedConnection)
|
||||||
@ -172,6 +172,7 @@ class EbookViewer(MainWindow):
|
|||||||
self.web_view.quit.connect(self.quit, type=Qt.QueuedConnection)
|
self.web_view.quit.connect(self.quit, type=Qt.QueuedConnection)
|
||||||
self.web_view.shortcuts_changed.connect(self.shortcuts_changed)
|
self.web_view.shortcuts_changed.connect(self.shortcuts_changed)
|
||||||
self.web_view.scrollbar_context_menu.connect(self.scrollbar_context_menu)
|
self.web_view.scrollbar_context_menu.connect(self.scrollbar_context_menu)
|
||||||
|
self.web_view.close_prep_finished.connect(self.close_prep_finished)
|
||||||
self.actions_toolbar.initialize(self.web_view, self.search_dock.toggleViewAction())
|
self.actions_toolbar.initialize(self.web_view, self.search_dock.toggleViewAction())
|
||||||
self.setCentralWidget(self.web_view)
|
self.setCentralWidget(self.web_view)
|
||||||
self.loading_overlay = LoadingOverlay(self)
|
self.loading_overlay = LoadingOverlay(self)
|
||||||
@ -568,7 +569,24 @@ class EbookViewer(MainWindow):
|
|||||||
def quit(self):
|
def quit(self):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
def force_close(self):
|
||||||
|
if not self.close_forced:
|
||||||
|
self.close_forced = True
|
||||||
|
self.quit()
|
||||||
|
|
||||||
|
def close_prep_finished(self, cfi):
|
||||||
|
if cfi:
|
||||||
|
self.cfi_changed(cfi)
|
||||||
|
self.force_close()
|
||||||
|
|
||||||
def closeEvent(self, ev):
|
def closeEvent(self, ev):
|
||||||
|
if self.current_book_data and self.web_view.view_is_ready and not self.close_forced:
|
||||||
|
ev.ignore()
|
||||||
|
if not self.shutting_down:
|
||||||
|
self.shutting_down = True
|
||||||
|
QTimer.singleShot(2000, self.force_close)
|
||||||
|
self.web_view.prepare_for_close()
|
||||||
|
return
|
||||||
self.shutting_down = True
|
self.shutting_down = True
|
||||||
self.search_widget.shutdown()
|
self.search_widget.shutdown()
|
||||||
try:
|
try:
|
||||||
|
@ -273,6 +273,7 @@ class ViewerBridge(Bridge):
|
|||||||
quit = from_js()
|
quit = from_js()
|
||||||
customize_toolbar = from_js()
|
customize_toolbar = from_js()
|
||||||
scrollbar_context_menu = from_js(object, object, object)
|
scrollbar_context_menu = from_js(object, object, object)
|
||||||
|
close_prep_finished = from_js(object)
|
||||||
|
|
||||||
create_view = to_js()
|
create_view = to_js()
|
||||||
start_book_load = to_js()
|
start_book_load = to_js()
|
||||||
@ -286,6 +287,7 @@ class ViewerBridge(Bridge):
|
|||||||
trigger_shortcut = to_js()
|
trigger_shortcut = to_js()
|
||||||
set_system_palette = to_js()
|
set_system_palette = to_js()
|
||||||
show_search_result = to_js()
|
show_search_result = to_js()
|
||||||
|
prepare_for_close = to_js()
|
||||||
|
|
||||||
|
|
||||||
def apply_font_settings(page_or_view):
|
def apply_font_settings(page_or_view):
|
||||||
@ -445,6 +447,7 @@ class WebView(RestartingWebEngineView):
|
|||||||
quit = pyqtSignal()
|
quit = pyqtSignal()
|
||||||
customize_toolbar = pyqtSignal()
|
customize_toolbar = pyqtSignal()
|
||||||
scrollbar_context_menu = pyqtSignal(object, object, object)
|
scrollbar_context_menu = pyqtSignal(object, object, object)
|
||||||
|
close_prep_finished = pyqtSignal(object)
|
||||||
shortcuts_changed = pyqtSignal(object)
|
shortcuts_changed = pyqtSignal(object)
|
||||||
paged_mode_changed = pyqtSignal()
|
paged_mode_changed = pyqtSignal()
|
||||||
standalone_misc_settings_changed = pyqtSignal(object)
|
standalone_misc_settings_changed = pyqtSignal(object)
|
||||||
@ -463,6 +466,7 @@ class WebView(RestartingWebEngineView):
|
|||||||
self.show_home_page_on_ready = True
|
self.show_home_page_on_ready = True
|
||||||
self._size_hint = QSize(int(w/3), int(w/2))
|
self._size_hint = QSize(int(w/3), int(w/2))
|
||||||
self._page = WebPage(self)
|
self._page = WebPage(self)
|
||||||
|
self.view_is_ready = False
|
||||||
self.bridge.bridge_ready.connect(self.on_bridge_ready)
|
self.bridge.bridge_ready.connect(self.on_bridge_ready)
|
||||||
self.bridge.view_created.connect(self.on_view_created)
|
self.bridge.view_created.connect(self.on_view_created)
|
||||||
self.bridge.content_file_changed.connect(self.on_content_file_changed)
|
self.bridge.content_file_changed.connect(self.on_content_file_changed)
|
||||||
@ -494,6 +498,7 @@ class WebView(RestartingWebEngineView):
|
|||||||
self.bridge.quit.connect(self.quit)
|
self.bridge.quit.connect(self.quit)
|
||||||
self.bridge.customize_toolbar.connect(self.customize_toolbar)
|
self.bridge.customize_toolbar.connect(self.customize_toolbar)
|
||||||
self.bridge.scrollbar_context_menu.connect(self.scrollbar_context_menu)
|
self.bridge.scrollbar_context_menu.connect(self.scrollbar_context_menu)
|
||||||
|
self.bridge.close_prep_finished.connect(self.close_prep_finished)
|
||||||
self.bridge.export_shortcut_map.connect(self.set_shortcut_map)
|
self.bridge.export_shortcut_map.connect(self.set_shortcut_map)
|
||||||
self.shortcut_map = {}
|
self.shortcut_map = {}
|
||||||
self.bridge.report_cfi.connect(self.call_callback)
|
self.bridge.report_cfi.connect(self.call_callback)
|
||||||
@ -578,6 +583,7 @@ class WebView(RestartingWebEngineView):
|
|||||||
|
|
||||||
def on_view_created(self, data):
|
def on_view_created(self, data):
|
||||||
self.view_created.emit(data)
|
self.view_created.emit(data)
|
||||||
|
self.view_is_ready = True
|
||||||
|
|
||||||
def on_content_file_changed(self, data):
|
def on_content_file_changed(self, data):
|
||||||
self.current_content_file = data
|
self.current_content_file = data
|
||||||
@ -669,3 +675,6 @@ class WebView(RestartingWebEngineView):
|
|||||||
|
|
||||||
def palette_changed(self):
|
def palette_changed(self):
|
||||||
self.execute_when_ready('set_system_palette', system_colors())
|
self.execute_when_ready('set_system_palette', system_colors())
|
||||||
|
|
||||||
|
def prepare_for_close(self):
|
||||||
|
self.execute_when_ready('prepare_for_close')
|
||||||
|
@ -172,7 +172,7 @@ class View:
|
|||||||
self.current_progress_frac = self.current_file_progress_frac = 0
|
self.current_progress_frac = self.current_file_progress_frac = 0
|
||||||
self.current_toc_node = self.current_toc_toplevel_node = None
|
self.current_toc_node = self.current_toc_toplevel_node = None
|
||||||
self.report_cfi_callbacks = {}
|
self.report_cfi_callbacks = {}
|
||||||
self.show_chrome_counter = 0
|
self.get_cfi_counter = 0
|
||||||
self.show_loading_callback_timer = None
|
self.show_loading_callback_timer = None
|
||||||
self.timer_ids = {'clock': 0}
|
self.timer_ids = {'clock': 0}
|
||||||
self.book_scrollbar = BookScrollbar(self)
|
self.book_scrollbar = BookScrollbar(self)
|
||||||
@ -521,12 +521,11 @@ class View:
|
|||||||
self.iframe.contentWindow.focus()
|
self.iframe.contentWindow.focus()
|
||||||
|
|
||||||
def show_chrome(self, data):
|
def show_chrome(self, data):
|
||||||
self.show_chrome_counter += 1
|
|
||||||
elements = {}
|
elements = {}
|
||||||
if data and data.elements:
|
if data and data.elements:
|
||||||
elements = data.elements
|
elements = data.elements
|
||||||
initial_panel = data?.initial_panel or None
|
initial_panel = data?.initial_panel or None
|
||||||
self.get_current_cfi('show-chrome-' + self.show_chrome_counter, self.do_show_chrome.bind(None, elements, initial_panel))
|
self.get_current_cfi('show-chrome', self.do_show_chrome.bind(None, elements, initial_panel))
|
||||||
|
|
||||||
def do_show_chrome(self, elements, initial_panel, request_id, cfi_data):
|
def do_show_chrome(self, elements, initial_panel, request_id, cfi_data):
|
||||||
self.hide_overlays()
|
self.hide_overlays()
|
||||||
@ -536,6 +535,13 @@ class View:
|
|||||||
else:
|
else:
|
||||||
self.overlay.show(elements)
|
self.overlay.show(elements)
|
||||||
|
|
||||||
|
def prepare_for_close(self):
|
||||||
|
|
||||||
|
def close_prepared(request_id, cfi_data):
|
||||||
|
ui_operations.close_prep_finished(cfi_data.cfi)
|
||||||
|
|
||||||
|
self.get_current_cfi('prepare-close', close_prepared)
|
||||||
|
|
||||||
def show_search(self):
|
def show_search(self):
|
||||||
self.hide_overlays()
|
self.hide_overlays()
|
||||||
if runtime.is_standalone_viewer:
|
if runtime.is_standalone_viewer:
|
||||||
@ -903,6 +909,8 @@ class View:
|
|||||||
self.goto_named_destination(toc_node.dest, toc_node.frag)
|
self.goto_named_destination(toc_node.dest, toc_node.frag)
|
||||||
|
|
||||||
def get_current_cfi(self, request_id, callback):
|
def get_current_cfi(self, request_id, callback):
|
||||||
|
self.get_cfi_counter += 1
|
||||||
|
request_id += ':' + self.get_cfi_counter
|
||||||
self.report_cfi_callbacks[request_id] = callback
|
self.report_cfi_callbacks[request_id] = callback
|
||||||
self.iframe_wrapper.send_message('get_current_cfi', request_id=request_id)
|
self.iframe_wrapper.send_message('get_current_cfi', request_id=request_id)
|
||||||
|
|
||||||
@ -923,7 +931,7 @@ class View:
|
|||||||
def on_report_cfi(self, data):
|
def on_report_cfi(self, data):
|
||||||
cb = self.report_cfi_callbacks[data.request_id]
|
cb = self.report_cfi_callbacks[data.request_id]
|
||||||
if cb:
|
if cb:
|
||||||
cb(data.request_id, {
|
cb(data.request_id.rpartition(':')[0], {
|
||||||
'cfi': data.cfi,
|
'cfi': data.cfi,
|
||||||
'progress_frac': data.progress_frac,
|
'progress_frac': data.progress_frac,
|
||||||
'file_progress_frac': data.file_progress_frac,
|
'file_progress_frac': data.file_progress_frac,
|
||||||
|
@ -293,6 +293,12 @@ def show_search_result(sr):
|
|||||||
if view:
|
if view:
|
||||||
view.show_search_result(sr)
|
view.show_search_result(sr)
|
||||||
|
|
||||||
|
@from_python
|
||||||
|
def prepare_for_close():
|
||||||
|
if view:
|
||||||
|
view.prepare_for_close()
|
||||||
|
else:
|
||||||
|
ui_operations.close_prep_finished(None)
|
||||||
|
|
||||||
def onerror(msg, script_url, line_number, column_number, error_object):
|
def onerror(msg, script_url, line_number, column_number, error_object):
|
||||||
if not error_object:
|
if not error_object:
|
||||||
@ -405,6 +411,8 @@ if window is window.top:
|
|||||||
to_python.search_result_not_found(sr)
|
to_python.search_result_not_found(sr)
|
||||||
ui_operations.scrollbar_context_menu = def(x, y, frac):
|
ui_operations.scrollbar_context_menu = def(x, y, frac):
|
||||||
to_python.scrollbar_context_menu(x, y, frac)
|
to_python.scrollbar_context_menu(x, y, frac)
|
||||||
|
ui_operations.close_prep_finished = def(cfi):
|
||||||
|
to_python.close_prep_finished(cfi)
|
||||||
|
|
||||||
document.body.appendChild(E.div(id='view'))
|
document.body.appendChild(E.div(id='view'))
|
||||||
window.onerror = onerror
|
window.onerror = onerror
|
||||||
|
Loading…
x
Reference in New Issue
Block a user