From bc83a98ddff9d9e9a8ddc90a7e2957eb936f842d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 5 Nov 2019 09:55:15 +0530 Subject: [PATCH] Viewer: When resizing and the resizing back to the old size ensure we return to the same page in paged mode. Fixes #1847507 [Viewer: when ToC is opened and closed, it doesn't return to same position](https://bugs.launchpad.net/calibre/+bug/1847507) --- src/pyj/read_book/iframe.pyj | 8 +++-- src/pyj/read_book/paged_mode.pyj | 56 +++++++++++++++++++++++++++++--- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/src/pyj/read_book/iframe.pyj b/src/pyj/read_book/iframe.pyj index 114f8b467a..649a5a4ebb 100644 --- a/src/pyj/read_book/iframe.pyj +++ b/src/pyj/read_book/iframe.pyj @@ -24,7 +24,8 @@ from read_book.paged_mode import ( anchor_funcs as paged_anchor_funcs, calc_columns_per_screen, current_cfi, handle_gesture as paged_handle_gesture, handle_shortcut as paged_handle_shortcut, jump_to_cfi as paged_jump_to_cfi, layout as paged_layout, - onwheel as paged_onwheel, progress_frac, reset_paged_mode_globals, + onwheel as paged_onwheel, prepare_for_resize as paged_prepare_for_resize, + progress_frac, reset_paged_mode_globals, resize_done as paged_resize_done, scroll_by_page as paged_scroll_by_page, scroll_to_elem, scroll_to_fraction as paged_scroll_to_fraction, snap_to_selection ) @@ -361,13 +362,16 @@ class IframeBoss: if scroll_viewport.width() is self.last_window_width and scroll_viewport.height() is self.last_window_height: # Safari at least, generates lots of spurious resize events return - self.last_window_width, self.last_window_height = scroll_viewport.width(), scroll_viewport.height() if current_layout_mode() is not 'flow': + paged_prepare_for_resize(self.last_window_width, self.last_window_height) self.do_layout(self.is_titlepage) + self.last_window_width, self.last_window_height = scroll_viewport.width(), scroll_viewport.height() if self.last_cfi: cfi = self.last_cfi[len('epubcfi(/'):-1].partition('/')[2] if cfi: paged_jump_to_cfi('/' + cfi) + if current_layout_mode() is not 'flow': + paged_resize_done() self.update_cfi() self.update_toc_position() diff --git a/src/pyj/read_book/paged_mode.pyj b/src/pyj/read_book/paged_mode.pyj index 8d51febaec..84918b18e8 100644 --- a/src/pyj/read_book/paged_mode.pyj +++ b/src/pyj/read_book/paged_mode.pyj @@ -58,14 +58,15 @@ def create_page_div(elem): _in_paged_mode = False def in_paged_mode(): return _in_paged_mode -col_width = screen_width = screen_height = cols_per_screen = gap = col_and_gap = number_of_cols = 0 +col_width = screen_width = screen_height = cols_per_screen = gap = col_and_gap = number_of_cols = last_scrolled_to_column = 0 is_full_screen_layout = False def reset_paged_mode_globals(): - nonlocal _in_paged_mode, col_width, col_and_gap, screen_height, gap, screen_width, is_full_screen_layout, cols_per_screen, number_of_cols + nonlocal _in_paged_mode, col_width, col_and_gap, screen_height, gap, screen_width, is_full_screen_layout, cols_per_screen, number_of_cols, last_scrolled_to_column scroll_viewport.reset_globals() - col_width = screen_width = screen_height = cols_per_screen = gap = col_and_gap = number_of_cols = 0 + col_width = screen_width = screen_height = cols_per_screen = gap = col_and_gap = number_of_cols = last_scrolled_to_column = 0 is_full_screen_layout = _in_paged_mode = False + resize_manager.reset() def column_at(xpos): # Return the (zero-based) number of the column that contains xpos @@ -130,7 +131,7 @@ def calc_columns_per_screen(): return cps -def layout(is_single_page): +def layout(is_single_page, on_resize): nonlocal _in_paged_mode, col_width, col_and_gap, screen_height, gap, screen_width, is_full_screen_layout, cols_per_screen body_style = window.getComputedStyle(document.body) first_layout = not _in_paged_mode @@ -151,7 +152,7 @@ def layout(is_single_page): if not single_screen and cps > 1: num = cps - 1 elems = document.querySelectorAll('body > *') - if elems.length == 1: + if elems.length is 1: # Workaround for the case when the content is wrapped in a # 100% height
. This causes the generated page divs to # not be in the correct location, at least in WebKit. See @@ -242,12 +243,15 @@ def scroll_to_offset(x): def scroll_to_column(number, notify=False, duration=1000): + nonlocal last_scrolled_to_column + last_scrolled_to_column = number pos = number * col_and_gap limit = scroll_viewport.paged_content_width() - screen_width pos = min(pos, limit) scroll_to_offset(pos) def scroll_to_xpos(xpos, notify=False, duration=1000): + nonlocal last_scrolled_to_column # Scroll so that the column containing xpos is the left most column in # the viewport if jstype(xpos) is not 'number': @@ -255,6 +259,7 @@ def scroll_to_xpos(xpos, notify=False, duration=1000): return if is_full_screen_layout: scroll_to_offset(0) + last_scrolled_to_column = 0 return scroll_to_column(column_at(xpos), notify=notify, duration=duration) @@ -565,3 +570,44 @@ anchor_funcs = { return a - b , } + + +class ResizeManager: + + def __init__(self): + self.reset() + + def reset(self): + self.resize_in_progress = None + self.last_transition = None + + def start_resize(self, width, height): + self.resize_in_progress = {'width': width, 'height': height, 'column': last_scrolled_to_column} + + def end_resize(self): + if not self.resize_in_progress: + return + rp, self.resize_in_progress = self.resize_in_progress, None + transition = {'before': rp, 'after': { + 'width': scroll_viewport.width(), 'height': scroll_viewport.height(), 'column': last_scrolled_to_column}} + if self.is_inverse_transition(transition): + if transition.after.column is not self.last_transition.before.column: + self.scroll_to_column(transition.after.column) + transition.after.column = last_scrolled_to_column + self.last_transition = transition + + def is_inverse_transition(self, transition): + p = self.last_transition + if not p: + return False + p = p.after + n = transition.before + return p.column is n.column and p.width is n.width and p.height is n.height + + +resize_manager = ResizeManager() +def prepare_for_resize(width, height): + resize_manager.start_resize(width, height) + +def resize_done(): + resize_manager.end_resize()