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
|
# 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.
|
# 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 {{{
|
# CFI escaping {{{
|
||||||
escape_pat = /[\[\],^();~@!-]/g
|
escape_pat = /[\[\],^();~@!-]/g
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# 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 dom import set_css
|
||||||
from read_book.globals import get_boss, scroll_viewport
|
|
||||||
from keycodes import get_key
|
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
|
from utils import document_height, document_width, viewport_to_document
|
||||||
|
|
||||||
|
|
||||||
def flow_to_scroll_fraction(frac):
|
def flow_to_scroll_fraction(frac):
|
||||||
scroll_viewport.scroll_to(0, document_height() * 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 aes import GCM, random_bytes
|
||||||
from encodings import hexlify
|
from encodings import hexlify
|
||||||
from gettext import gettext as _, register_callback, gettext as gt
|
from gettext import gettext as _, register_callback, gettext as gt
|
||||||
from utils import is_ios
|
|
||||||
|
|
||||||
_boss = None
|
_boss = None
|
||||||
|
|
||||||
@ -39,97 +38,11 @@ class Messenger:
|
|||||||
messenger = Messenger()
|
messenger = Messenger()
|
||||||
iframe_id = 'read-book-iframe'
|
iframe_id = 'read-book-iframe'
|
||||||
uid = 'calibre-' + hexlify(random_bytes(12))
|
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():
|
def current_layout_mode():
|
||||||
return current_layout_mode.value
|
return current_layout_mode.value
|
||||||
@ -137,20 +50,7 @@ current_layout_mode.value = 'flow'
|
|||||||
|
|
||||||
def set_layout_mode(val):
|
def set_layout_mode(val):
|
||||||
current_layout_mode.value = val
|
current_layout_mode.value = val
|
||||||
if val is 'flow':
|
viewport_mode_changer()(val)
|
||||||
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
|
|
||||||
|
|
||||||
def current_spine_item():
|
def current_spine_item():
|
||||||
return current_spine_item.value
|
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
|
layout as flow_layout, scroll_by_page as flow_scroll_by_page
|
||||||
)
|
)
|
||||||
from read_book.globals import (
|
from read_book.globals import (
|
||||||
current_book, current_layout_mode, current_spine_item, scroll_viewport, set_boss,
|
current_book, current_layout_mode, current_spine_item, set_boss,
|
||||||
set_current_spine_item, set_layout_mode, window_height, window_width
|
set_current_spine_item, set_layout_mode
|
||||||
)
|
)
|
||||||
from read_book.mathjax import apply_mathjax
|
from read_book.mathjax import apply_mathjax
|
||||||
from read_book.paged_mode import (
|
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.settings import apply_settings, opts
|
||||||
from read_book.toc import update_visible_toc_anchors
|
from read_book.toc import update_visible_toc_anchors
|
||||||
from read_book.touch import create_handlers as create_touch_handlers
|
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
|
from utils import debounce, html_escape, is_ios
|
||||||
|
|
||||||
FORCE_FLOW_MODE = False
|
FORCE_FLOW_MODE = False
|
||||||
@ -118,7 +119,7 @@ class IframeBoss:
|
|||||||
|
|
||||||
def initialize(self, data):
|
def initialize(self, data):
|
||||||
self.gcm_from_parent, self.gcm_to_parent = GCM(data.secret.subarray(0, 32)), GCM(data.secret.subarray(32))
|
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:
|
if data.translations:
|
||||||
install(data.translations)
|
install(data.translations)
|
||||||
window.onerror = self.onerror
|
window.onerror = self.onerror
|
||||||
@ -224,7 +225,7 @@ class IframeBoss:
|
|||||||
# document.body.appendChild(
|
# document.body.appendChild(
|
||||||
# E.style() # TODO: User style sheet
|
# 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_colors()
|
||||||
self.apply_font_size()
|
self.apply_font_size()
|
||||||
self.do_layout()
|
self.do_layout()
|
||||||
@ -313,10 +314,10 @@ class IframeBoss:
|
|||||||
self.onresize_stage2()
|
self.onresize_stage2()
|
||||||
|
|
||||||
def onresize_stage2(self):
|
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
|
# Safari at least, generates lots of spurious resize events
|
||||||
return
|
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':
|
if current_layout_mode() is not 'flow':
|
||||||
self.do_layout()
|
self.do_layout()
|
||||||
if self.last_cfi:
|
if self.last_cfi:
|
||||||
@ -327,7 +328,7 @@ class IframeBoss:
|
|||||||
self.update_toc_position()
|
self.update_toc_position()
|
||||||
|
|
||||||
def received_window_size(self, data):
|
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:
|
if self.content_ready:
|
||||||
self.onresize_stage2()
|
self.onresize_stage2()
|
||||||
|
|
||||||
|
@ -2,14 +2,20 @@
|
|||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
from __python__ import hash_literals
|
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
|
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):
|
def first_child(parent):
|
||||||
c = parent.firstChild
|
c = parent.firstChild
|
||||||
|
@ -8,7 +8,8 @@ from elementmaker import E
|
|||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from modals import error_dialog
|
from modals import error_dialog
|
||||||
from widgets import create_tree, find_text_in_tree, scroll_tree_item_into_view
|
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):
|
def update_visible_toc_nodes(visible_anchors):
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
|
# 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 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
|
HOLD_THRESHOLD = 750 # milliseconds
|
||||||
TAP_THRESHOLD = 5 # pixels
|
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