From bdf4e01d7a2f014dc6b2abf958d2c93e4cf1b6ff Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 1 Feb 2015 10:22:17 +0530 Subject: [PATCH] Make jumping to a cfi pseudo synchronous by spinning the event loop while waiting for the scroll to finish --- resources/compiled_coffeescript.zip | Bin 102583 -> 102740 bytes src/calibre/ebooks/oeb/display/paged.coffee | 4 ++- src/calibre/gui2/viewer/documentview.py | 6 ++++ src/calibre/gui2/viewer/main.py | 15 +++------- src/calibre/gui2/viewer/position.py | 29 +++++++++++++++++--- 5 files changed, 38 insertions(+), 16 deletions(-) diff --git a/resources/compiled_coffeescript.zip b/resources/compiled_coffeescript.zip index 05e40b1bb4765e3b20f2f083cbfee0025ef3d849..31540747f5a07b03a39d46002cf1f709e87e49ef 100644 GIT binary patch delta 385 zcmdnKknPGMwhc=*@y!TwblagXx5WujXS^ z;>=7_&;au%7k(DyfXFD=PQS>^C^fn6Kl@~vb;8s4@iE#l#!p`GO=P+RKcoC)ss94g z{rDNh#S|1k+RHQZQu5373M%81iZWBuQzw7u7M*O*%0B%-JtLQNR%vcQd`W(Ma$075 zT4r8maYkwi#Kx4#jc5;vRTFkTk7^h47G0I8=c(bxG009>ezG7lv2e#VgLJ>86a)Axh8=Xx1^r!)04_DqlKV-(Rav$QZxN-{A|wJrG&^Vyp!K D`!{|~ delta 312 zcmcbzh;92qwhc=*@f``ZcS}6|x3VmifuSsQ`acOq$?5#Pj2xRUZ(0$}tEo`Ur2qzH ziA9qgBSojbXkg@<{)Ue+aPq%ycEDhfiLT3V_h>o$5nWed*QEIY* zfrUj{vO%h`k%5^}qN$llnu$e9vYBz3WtvG^nz53O0>}`>=>j1A3;G#*rmyQ`44Ga( afzcJjVxKNKk + jump_to_cfi: (cfi, job_id=-1) -> # Jump to the position indicated by the specified conformal fragment # indicator (requires the cfi.coffee library). When in paged mode, the # scroll is performed so that the column containing the position @@ -540,6 +540,8 @@ class PagedDisplay this.scroll_to_xpos(x) else window.scrollTo(0, y) + if window.py_bridge + window.py_bridge.jump_to_cfi_finished(job_id) ) current_cfi: () -> diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py index dbac9c7b97..1fd474c2bb 100644 --- a/src/calibre/gui2/viewer/documentview.py +++ b/src/calibre/gui2/viewer/documentview.py @@ -93,6 +93,7 @@ class Document(QWebPage): # {{{ # javascript, get/set the value as: py_bridge.value self.bridge_value = None self.first_load = True + self.jump_to_cfi_listeners = set() self.debug_javascript = debug_javascript self.anchor_positions = {} @@ -336,6 +337,11 @@ class Document(QWebPage): # {{{ def debug(self, msg): prints(unicode(msg)) + @pyqtSlot(int) + def jump_to_cfi_finished(self, job_id): + for l in self.jump_to_cfi_listeners: + l(job_id) + def reference_mode(self, enable): self.javascript(('enter' if enable else 'leave')+'_reference_mode()') diff --git a/src/calibre/gui2/viewer/main.py b/src/calibre/gui2/viewer/main.py index 6d1afa8bef..41e3dc343c 100644 --- a/src/calibre/gui2/viewer/main.py +++ b/src/calibre/gui2/viewer/main.py @@ -722,23 +722,16 @@ class EbookViewer(MainWindow): if self.isFullScreen(): self.relayout_fullscreen_labels() self.view.document.after_resize() - # For some reason scroll_fraction returns incorrect results in paged - # mode for some time after a resize is finished. No way of knowing - # exactly how long, so we update it in a second, in the hopes that it - # will be enough *most* of the time. - QTimer.singleShot(1000, self.scroll_after_resize_done) if not wmc: pre_footnote_pos = self.pre_footnote_toggle_position() if pre_footnote_pos is not None: self.view.document.page_number = pre_footnote_pos else: self.view.document.page_position.restore() - - def scroll_after_resize_done(self): - self.update_page_number() - if len(self.page_position_on_footnote_toggle) % 2 == 1: - self.page_position_on_footnote_toggle[-1] = self.page_position_on_footnote_toggle[-1]._replace( - after_resize_page_number=self.view.document.page_number) + self.update_page_number() + if len(self.page_position_on_footnote_toggle) % 2 == 1: + self.page_position_on_footnote_toggle[-1] = self.page_position_on_footnote_toggle[-1]._replace( + after_resize_page_number=self.view.document.page_number) def update_page_number(self): self.set_page_number(self.view.document.scroll_fraction) diff --git a/src/calibre/gui2/viewer/position.py b/src/calibre/gui2/viewer/position.py index 2bcc849821..d97348fc28 100644 --- a/src/calibre/gui2/viewer/position.py +++ b/src/calibre/gui2/viewer/position.py @@ -7,12 +7,19 @@ __license__ = 'GPL v3' __copyright__ = '2012, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import json +import json, time + +from PyQt5.Qt import QApplication, QEventLoop + +from calibre.constants import DEBUG class PagePosition(object): def __init__(self, document): self.document = document + document.jump_to_cfi_listeners.add(self) + self.cfi_job_id = 0 + self.pending_scrolls = set() @property def viewport_cfi(self): @@ -30,9 +37,22 @@ class PagePosition(object): def scroll_to_cfi(self, cfi): if cfi: + jid = self.cfi_job_id + self.cfi_job_id += 1 cfi = json.dumps(cfi) + self.pending_scrolls.add(jid) self.document.mainFrame().evaluateJavaScript( - 'paged_display.jump_to_cfi(%s)'%cfi) + 'paged_display.jump_to_cfi(%s, %d)' % (cfi, jid)) + # jump_to_cfi is async, so we wait for it to complete + st = time.time() + WAIT = 1 # seconds + while jid in self.pending_scrolls and time.time() - st < WAIT: + QApplication.processEvents(QEventLoop.ExcludeUserInputEvents | QEventLoop.ExcludeSocketNotifiers) + time.sleep(0.01) + if jid in self.pending_scrolls: + self.pending_scrolls.discard(jid) + if DEBUG: + print ('jump_to_cfi() failed to complete after %s seconds' % WAIT) @property def current_pos(self): @@ -47,6 +67,9 @@ class PagePosition(object): def __exit__(self, *args): self.restore() + def __call__(self, cfi_job_id): + self.pending_scrolls.discard(cfi_job_id) + def save(self, overwrite=True): if not overwrite and self._cpos is not None: return @@ -66,5 +89,3 @@ class PagePosition(object): def set_pos(self, pos): self._cpos = pos - -