mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Refactor scroll viewport handling
This commit is contained in:
parent
15b0d5dcc9
commit
bce5112268
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
115
src/pyj/read_book/viewport.pyj
Normal file
115
src/pyj/read_book/viewport.pyj
Normal 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)
|
Loading…
x
Reference in New Issue
Block a user