Refactor scroll viewport handling

This commit is contained in:
Kovid Goyal 2017-05-28 11:10:54 +05:30
parent 15b0d5dcc9
commit bce5112268
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
8 changed files with 151 additions and 125 deletions

View File

@ -21,7 +21,7 @@ from __python__ import hash_literals
# scroll_to(cfi): which scrolls the browser to a point corresponding to the
# given cfi, and returns the x and y co-ordinates of the point.
from read_book.globals import scroll_viewport
from read_book.viewport import scroll_viewport
# CFI escaping {{{
escape_pat = /[\[\],^();~@!-]/g

View File

@ -1,12 +1,14 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import hash_literals, bound_methods
from __python__ import bound_methods, hash_literals
from dom import set_css
from read_book.globals import get_boss, scroll_viewport
from keycodes import get_key
from read_book.globals import get_boss
from read_book.viewport import scroll_viewport
from utils import document_height, document_width, viewport_to_document
def flow_to_scroll_fraction(frac):
scroll_viewport.scroll_to(0, document_height() * frac)

View File

@ -5,7 +5,6 @@ from __python__ import hash_literals
from aes import GCM, random_bytes
from encodings import hexlify
from gettext import gettext as _, register_callback, gettext as gt
from utils import is_ios
_boss = None
@ -39,97 +38,11 @@ class Messenger:
messenger = Messenger()
iframe_id = 'read-book-iframe'
uid = 'calibre-' + hexlify(random_bytes(12))
scroll_viewport = {}
def flow_viewport_x():
return window.pageXOffset
def flow_viewport_y():
return window.pageYOffset
def flow_viewport_scroll_to(x, y):
window.scrollTo(x, y)
def flow_viewport_scroll_into_view(elem):
elem.scrollIntoView()
def flow_reset_globals():
pass
def flow_reset_transforms():
pass
def paged_viewport_y():
return 0
scroll_viewport.x = flow_viewport_x
scroll_viewport.y = flow_viewport_y
scroll_viewport.scroll_to = flow_viewport_scroll_to
scroll_viewport.scroll_into_view = flow_viewport_scroll_into_view
scroll_viewport.reset_globals = flow_reset_globals
scroll_viewport.reset_transforms = flow_reset_transforms
if is_ios:
window_width = def ():
return window_width.from_parent or window.innerWidth
window_height = def():
return window_height.from_parent or window.innerHeight
paged_viewport_scroll_to = def (x, y):
if x is 0:
document.documentElement.style.transform = 'none'
else:
x *= -1
document.documentElement.style.transform = f'translateX({x}px)'
boss = get_boss()
if boss:
boss.onscroll()
paged_viewport_x = def():
raw = document.documentElement.style.transform
if not raw or raw is 'none':
return 0
raw = raw[raw.indexOf('(') + 1:]
ans = parseInt(raw)
if isNaN(ans):
return 0
ans *= -1
return ans
paged_viewport_scroll_into_view = def(elem):
left = elem.offsetLeft
if left is None:
return # element has display: none
elem.scrollIntoView()
window.scrollTo(0, 0)
p = elem.offsetParent
while p:
left += p.offsetLeft
p = p.offsetParent
# left -= window_width() // 2
paged_viewport_scroll_to(max(0, left), 0)
paged_content_width = def():
return document.documentElement.scrollWidth
paged_reset_transforms = def():
document.documentElement.style.transform = 'none'
paged_reset_globals = def():
paged_reset_transforms()
else:
window_width = def():
return window.innerWidth
window_height = def():
return window.innerHeight
paged_viewport_scroll_to = flow_viewport_scroll_to
paged_viewport_x = flow_viewport_x
paged_viewport_scroll_into_view = flow_viewport_scroll_into_view
paged_reset_globals = flow_reset_globals
paged_content_width = def():
return document.documentElement.scrollWidth
paged_reset_transforms = def():
pass
scroll_viewport.width = window_width
scroll_viewport.height = window_height
scroll_viewport.paged_content_width = paged_content_width
def viewport_mode_changer(val):
if val:
viewport_mode_changer.val = val
return viewport_mode_changer.val
def current_layout_mode():
return current_layout_mode.value
@ -137,20 +50,7 @@ current_layout_mode.value = 'flow'
def set_layout_mode(val):
current_layout_mode.value = val
if val is 'flow':
scroll_viewport.x = flow_viewport_x
scroll_viewport.y = flow_viewport_y
scroll_viewport.scroll_to = flow_viewport_scroll_to
scroll_viewport.scroll_into_view = flow_viewport_scroll_into_view
scroll_viewport.reset_globals = flow_reset_globals
scroll_viewport.reset_transforms = flow_reset_transforms
else:
scroll_viewport.x = paged_viewport_x
scroll_viewport.y = paged_viewport_y
scroll_viewport.scroll_to = paged_viewport_scroll_to
scroll_viewport.scroll_into_view = paged_viewport_scroll_into_view
scroll_viewport.reset_globals = paged_reset_globals
scroll_viewport.reset_transforms = paged_reset_transforms
viewport_mode_changer()(val)
def current_spine_item():
return current_spine_item.value

View File

@ -13,8 +13,8 @@ from read_book.flow_mode import (
layout as flow_layout, scroll_by_page as flow_scroll_by_page
)
from read_book.globals import (
current_book, current_layout_mode, current_spine_item, scroll_viewport, set_boss,
set_current_spine_item, set_layout_mode, window_height, window_width
current_book, current_layout_mode, current_spine_item, set_boss,
set_current_spine_item, set_layout_mode
)
from read_book.mathjax import apply_mathjax
from read_book.paged_mode import (
@ -28,6 +28,7 @@ from read_book.resources import finalize_resources, unserialize_html
from read_book.settings import apply_settings, opts
from read_book.toc import update_visible_toc_anchors
from read_book.touch import create_handlers as create_touch_handlers
from read_book.viewport import scroll_viewport
from utils import debounce, html_escape, is_ios
FORCE_FLOW_MODE = False
@ -118,7 +119,7 @@ class IframeBoss:
def initialize(self, data):
self.gcm_from_parent, self.gcm_to_parent = GCM(data.secret.subarray(0, 32)), GCM(data.secret.subarray(32))
window_width.from_parent, window_height.from_parent = data.width, data.height
scroll_viewport.update_window_size(data.width, data.height)
if data.translations:
install(data.translations)
window.onerror = self.onerror
@ -224,7 +225,7 @@ class IframeBoss:
# document.body.appendChild(
# E.style() # TODO: User style sheet
# )
self.last_window_width, self.last_window_height = window_width(), window_height()
self.last_window_width, self.last_window_height = scroll_viewport.width(), scroll_viewport.height()
self.apply_colors()
self.apply_font_size()
self.do_layout()
@ -313,10 +314,10 @@ class IframeBoss:
self.onresize_stage2()
def onresize_stage2(self):
if window_width() is self.last_window_width and window_height() is self.last_window_height:
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 = window_width(), window_height()
self.last_window_width, self.last_window_height = scroll_viewport.width(), scroll_viewport.height()
if current_layout_mode() is not 'flow':
self.do_layout()
if self.last_cfi:
@ -327,7 +328,7 @@ class IframeBoss:
self.update_toc_position()
def received_window_size(self, data):
window_width.from_parent, window_height.from_parent = data.width, data.height
scroll_viewport.update_window_size(data.width, data.height)
if self.content_ready:
self.onresize_stage2()

View File

@ -2,14 +2,20 @@
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import hash_literals
from dom import set_css
from elementmaker import E
from keycodes import get_key
from read_book.cfi import scroll_to as cfi_scroll_to, at_point as cfi_at_point, at_current as cfi_at_current
from read_book.globals import get_boss, scroll_viewport
from read_book.settings import opts
import traceback
from utils import get_elem_data, set_elem_data, viewport_to_document, document_width
from elementmaker import E
from dom import set_css
from keycodes import get_key
from read_book.cfi import (
at_current as cfi_at_current, at_point as cfi_at_point,
scroll_to as cfi_scroll_to
)
from read_book.globals import get_boss
from read_book.settings import opts
from read_book.viewport import scroll_viewport
from utils import document_width, get_elem_data, set_elem_data, viewport_to_document
def first_child(parent):
c = parent.firstChild

View File

@ -8,7 +8,8 @@ from elementmaker import E
from gettext import gettext as _
from modals import error_dialog
from widgets import create_tree, find_text_in_tree, scroll_tree_item_into_view
from read_book.globals import toc_anchor_map, set_toc_anchor_map, current_spine_item, current_layout_mode, current_book, scroll_viewport
from read_book.globals import toc_anchor_map, set_toc_anchor_map, current_spine_item, current_layout_mode, current_book
from read_book.viewport import scroll_viewport
def update_visible_toc_nodes(visible_anchors):

View File

@ -1,9 +1,10 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import hash_literals, bound_methods
from __python__ import bound_methods, hash_literals
from read_book.globals import get_boss, scroll_viewport
from book_list.globals import get_read_ui
from read_book.globals import get_boss
from read_book.viewport import scroll_viewport
HOLD_THRESHOLD = 750 # milliseconds
TAP_THRESHOLD = 5 # pixels

View File

@ -0,0 +1,115 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import bound_methods, hash_literals
FUNCTIONS = 'x y scroll_to scroll_into_view reset_globals reset_transforms'.split(' ')
from read_book.globals import get_boss, viewport_mode_changer
from utils import is_ios
class ScrollViewport:
def __init__(self):
self.set_mode('flow')
self.window_width_from_parent = self.window_height_from_parent = None
def set_mode(self, mode):
prefix = ('flow' if mode is 'flow' else 'paged') + '_'
for attr in FUNCTIONS:
self[attr] = self[prefix + attr]
def flow_x(self):
return window.pageXOffset
def flow_y(self):
return window.pageYOffset
def paged_y(self):
return 0
def flow_scroll_to(self, x, y):
window.scrollTo(x, y)
def flow_scroll_into_view(self, elem):
elem.scrollIntoView()
def flow_reset_globals(self):
pass
def flow_reset_transforms(self):
pass
def paged_content_width(self):
return document.documentElement.scrollWidth
def update_window_size(self, w, h):
self.window_width_from_parent = w
self.window_height_from_parent = h
def width(self):
return window.innerWidth
def height(self):
return window.innerHeight
class IOSScrollViewport(ScrollViewport):
def width(self):
return self.window_width_from_parent or window.innerWidth
def height(self):
return self.window_height_from_parent or window.innerHeight
def _scroll_implementation(self, x):
if x is 0:
document.documentElement.style.transform = 'none'
else:
x *= -1
document.documentElement.style.transform = f'translateX({x}px)'
def paged_scroll_to(self, x, y):
self._scroll_implementation(x)
boss = get_boss()
if boss:
boss.onscroll()
def paged_x(self):
raw = document.documentElement.style.transform
if not raw or raw is 'none':
return 0
raw = raw[raw.indexOf('(') + 1:]
ans = parseInt(raw)
if isNaN(ans):
return 0
ans *= -1
return ans
def paged_scroll_into_view(self, elem):
left = elem.offsetLeft
if left is None:
return # element has display: none
elem.scrollIntoView()
window.scrollTo(0, 0)
p = elem.offsetParent
while p:
left += p.offsetLeft
p = p.offsetParent
# left -= window_width() // 2
self._scroll_implementation(max(0, left))
def paged_reset_transforms(self):
document.documentElement.style.transform = 'none'
def paged_reset_globals(self):
self.paged_reset_transforms()
if is_ios:
scroll_viewport = IOSScrollViewport()
else:
scroll_viewport = ScrollViewport()
for attr in FUNCTIONS:
if not scroll_viewport['paged_' + attr]:
scroll_viewport['paged_' + attr] = scroll_viewport[attr]
viewport_mode_changer(scroll_viewport.set_mode)