mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Additional scroll options...
Option for whether or not to load next file after reaching the start/end when using key based scrolling Configurable delay before loading the next file after reaching the end with auto-scroll
This commit is contained in:
parent
568d32ec16
commit
0a9e52d212
@ -51,9 +51,9 @@ def add_small_scroll(amt):
|
||||
|
||||
|
||||
def report_human_scroll(amt):
|
||||
if amt > 0:
|
||||
if abs(amt) > 0:
|
||||
h = scroll_viewport.height()
|
||||
is_large_scroll = (amt / h) >= 0.5
|
||||
is_large_scroll = (abs(amt) / h) >= 0.5
|
||||
if is_large_scroll:
|
||||
clear_small_scrolls()
|
||||
get_boss().report_human_scroll(amt / document_height())
|
||||
@ -180,10 +180,19 @@ def handle_shortcut(sc_name, evt):
|
||||
|
||||
|
||||
def layout(is_single_page):
|
||||
cancel_scroll()
|
||||
scroll_animator.wait = False
|
||||
scroll_animator.sync()
|
||||
set_css(document.body, margin='0', border_width='0', padding='0')
|
||||
line_height.doc_style = window.getComputedStyle(document.body)
|
||||
|
||||
# Pause auto-scroll while minimized
|
||||
document.addEventListener("visibilitychange", def():
|
||||
if (document.visibilityState is 'visible'):
|
||||
scroll_animator.sync()
|
||||
else:
|
||||
scroll_animator.pause()
|
||||
)
|
||||
|
||||
def cancel_scroll():
|
||||
scroll_animator.stop()
|
||||
|
||||
@ -202,16 +211,22 @@ class ScrollAnimator:
|
||||
return self.animation_id != None
|
||||
|
||||
def start(self, direction, auto):
|
||||
if self.wait:
|
||||
return
|
||||
|
||||
now = performance.now()
|
||||
self.end_time = now + self.DURATION
|
||||
clearTimeout(self.auto_timer)
|
||||
|
||||
if !self.is_running() or direction != self.direction or auto != self.auto:
|
||||
self.paused = self.direction if self.auto and not auto else False
|
||||
if self.auto and not auto:
|
||||
self.pause()
|
||||
self.stop()
|
||||
self.auto = auto
|
||||
self.direction = direction
|
||||
self.start_time = now
|
||||
self.start_offset = window.pageYOffset
|
||||
self.csi_idx = current_spine_item().index
|
||||
self.animation_id = window.requestAnimationFrame(self.auto_scroll if auto else self.smooth_scroll)
|
||||
|
||||
def smooth_scroll(self, ts):
|
||||
@ -221,18 +236,22 @@ class ScrollAnimator:
|
||||
scroll_target += Math.trunc(self.direction * progress * duration * line_height() * opts.lines_per_sec_smooth) / 1000
|
||||
|
||||
window.scrollTo(0, scroll_target)
|
||||
amt = window.pageYOffset - self.start_offset
|
||||
|
||||
if progress < 1:
|
||||
if is_scroll_end(scroll_target) and (not opts.scroll_stop_boundaries or (abs(amt) < 3 and duration is self.DURATION)):
|
||||
# "Turn the page" if stop at boundaries option is false or
|
||||
# this is a new scroll action and we were already at the end
|
||||
self.animation_id = None
|
||||
self.wait = True
|
||||
report_human_scroll(amt)
|
||||
get_boss().send_message('next_spine_item', previous=self.direction is DIRECTION.Up)
|
||||
elif progress < 1:
|
||||
self.animation_id = window.requestAnimationFrame(self.smooth_scroll)
|
||||
elif self.paused:
|
||||
self.resume()
|
||||
else:
|
||||
amt = window.pageYOffset - self.start_offset
|
||||
if abs(amt) < 3 and duration is self.DURATION and is_scroll_end(scroll_target):
|
||||
get_boss().send_message('next_spine_item', previous=self.direction is DIRECTION.Up)
|
||||
elif self.paused:
|
||||
self.start(self.paused, True)
|
||||
else:
|
||||
self.animation_id = None
|
||||
report_human_scroll(amt)
|
||||
self.animation_id = None
|
||||
report_human_scroll(amt)
|
||||
|
||||
def auto_scroll(self, ts):
|
||||
elapsed = max(0, ts - self.start_time) # max to account for jitter
|
||||
@ -247,24 +266,45 @@ class ScrollAnimator:
|
||||
self.sync(ts)
|
||||
|
||||
if scroll_finished:
|
||||
self.stop()
|
||||
self.pause()
|
||||
if opts.scroll_auto_boundary_delay:
|
||||
self.auto_timer = setTimeout(def(): get_boss().send_message('next_spine_item', previous=self.direction is DIRECTION.Up);, opts.scroll_auto_boundary_delay * 1000)
|
||||
else:
|
||||
self.animation_id = window.requestAnimationFrame(self.auto_scroll)
|
||||
|
||||
def report(self):
|
||||
amt = window.pageYOffset - self.start_offset
|
||||
if abs(amt) > 0 and self.csi_idx is current_spine_item().index:
|
||||
report_human_scroll(amt)
|
||||
|
||||
def sync(self, ts):
|
||||
if self.auto:
|
||||
report_human_scroll(window.pageYOffset - self.start_offset)
|
||||
self.report()
|
||||
self.csi_idx = current_spine_item().index
|
||||
self.start_time = ts or performance.now()
|
||||
self.start_offset = window.pageYOffset
|
||||
else:
|
||||
self.resume()
|
||||
|
||||
def stop(self):
|
||||
self.auto = False
|
||||
if self.animation_id is not None:
|
||||
window.cancelAnimationFrame(self.animation_id)
|
||||
self.animation_id = None
|
||||
amt = window.pageYOffset - self.start_offset
|
||||
if amt > 0:
|
||||
report_human_scroll(amt)
|
||||
self.report()
|
||||
|
||||
def pause(self):
|
||||
if self.auto:
|
||||
self.paused = self.direction
|
||||
self.stop()
|
||||
else:
|
||||
self.paused = False
|
||||
|
||||
# Resume auto-scroll
|
||||
def resume(self):
|
||||
if self.paused:
|
||||
self.start(self.paused, True)
|
||||
self.paused = False
|
||||
|
||||
scroll_animator = ScrollAnimator()
|
||||
|
||||
|
@ -270,7 +270,6 @@ class IframeBoss:
|
||||
self.last_window_width, self.last_window_height = scroll_viewport.width(), scroll_viewport.height()
|
||||
apply_settings()
|
||||
fix_fullscreen_svg_images()
|
||||
self.do_layout(self.is_titlepage)
|
||||
if self.mathjax:
|
||||
return apply_mathjax(self.mathjax, self.book.manifest.link_uid, self.content_loaded_stage2)
|
||||
# window.setTimeout(self.content_loaded_stage2, 1000)
|
||||
@ -307,6 +306,7 @@ class IframeBoss:
|
||||
if si:
|
||||
self.length_before += files[si]?.length or 0
|
||||
self.onscroll()
|
||||
self.do_layout(self.is_titlepage)
|
||||
self.send_message('content_loaded', progress_frac=self.calculate_progress_frac(), file_progress_frac=progress_frac())
|
||||
self.last_cfi = None
|
||||
window.setTimeout(self.update_cfi, 0)
|
||||
|
@ -16,6 +16,9 @@ CONTAINER = unique_id('standalone-scrolling-settings')
|
||||
MIN_SCROLL_SPEED_AUTO = 0.25
|
||||
MAX_SCROLL_SPEED_AUTO = 5
|
||||
|
||||
MIN_SCROLL_AUTO_DELAY = 0
|
||||
MAX_SCROLL_AUTO_DELAY = 10
|
||||
|
||||
MIN_SCROLL_SPEED_SMOOTH = 10
|
||||
MAX_SCROLL_SPEED_SMOOTH = 50
|
||||
|
||||
@ -57,7 +60,7 @@ def create_scrolling_panel(container, apply_func, cancel_func):
|
||||
ans = E.input(type='number', name=name, id=name)
|
||||
for key, val in Object.entries(kwargs):
|
||||
ans[key] = val
|
||||
ans.valueAsNumber = sd.get(name) or defaults[name]
|
||||
ans.valueAsNumber = sd.get(name, defaults[name])
|
||||
return E.label("for"=name, text), ans
|
||||
|
||||
container.appendChild(E.div(style='margin-top:1ex', _('Control how mouse based scrolling works in paged mode')))
|
||||
@ -68,8 +71,12 @@ def create_scrolling_panel(container, apply_func, cancel_func):
|
||||
|
||||
container.appendChild(E.hr())
|
||||
container.appendChild(E.div(style='margin-top:1ex', _('Control how smooth scrolling works in flow mode')))
|
||||
container.appendChild(cb(
|
||||
'scroll_stop_boundaries',
|
||||
_('Stop at file boundaries when continuous scrolling while holding down the scroll key')
|
||||
))
|
||||
container.appendChild(
|
||||
E.div(style='display:grid;margin-top:1ex;align-items:center;grid-template-columns:auto auto;grid-gap:1ex;justify-content:flex-start;',
|
||||
E.div(style='display:grid;margin-top:1ex;align-items:center;grid-template-columns:25em min-content;grid-gap:1ex',
|
||||
*spinner(
|
||||
'lines_per_sec_smooth',
|
||||
_('Smooth scrolling speed in lines/sec'),
|
||||
@ -83,6 +90,13 @@ def create_scrolling_panel(container, apply_func, cancel_func):
|
||||
step=MIN_SCROLL_SPEED_AUTO,
|
||||
min=MIN_SCROLL_SPEED_AUTO,
|
||||
max=MAX_SCROLL_SPEED_AUTO
|
||||
),
|
||||
*spinner(
|
||||
'scroll_auto_boundary_delay',
|
||||
_('Seconds to wait before loading the next file after auto-scroll reaches the end; 0 to disable'),
|
||||
step=0.25,
|
||||
min=MIN_SCROLL_AUTO_DELAY,
|
||||
max=MAX_SCROLL_AUTO_DELAY
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -23,6 +23,8 @@ def update_settings(settings):
|
||||
opts.margin_right = max(0, settings.margin_right)
|
||||
opts.override_book_colors = settings.override_book_colors
|
||||
opts.paged_wheel_scrolls_by_screen = v'!!settings.paged_wheel_scrolls_by_screen'
|
||||
opts.scroll_auto_boundary_delay = settings.scroll_auto_boundary_delay
|
||||
opts.scroll_stop_boundaries = v'!!settings.scroll_stop_boundaries'
|
||||
opts.user_stylesheet = settings.user_stylesheet
|
||||
|
||||
update_settings()
|
||||
|
@ -712,6 +712,8 @@ class View:
|
||||
'paged_wheel_scrolls_by_screen': sd.get('paged_wheel_scrolls_by_screen'),
|
||||
'lines_per_sec_auto': sd.get('lines_per_sec_auto'),
|
||||
'lines_per_sec_smooth': sd.get('lines_per_sec_smooth'),
|
||||
'scroll_auto_boundary_delay': sd.get('scroll_auto_boundary_delay'),
|
||||
'scroll_stop_boundaries': sd.get('scroll_stop_boundaries'),
|
||||
}
|
||||
|
||||
def show_name(self, name, initial_position=None):
|
||||
|
@ -49,6 +49,8 @@ defaults = {
|
||||
'paged_margin_clicks_scroll_by_screen': True,
|
||||
'paged_wheel_scrolls_by_screen': False,
|
||||
'read_mode': 'paged',
|
||||
'scroll_auto_boundary_delay': 5,
|
||||
'scroll_stop_boundaries': False,
|
||||
'standalone_font_settings': {},
|
||||
'standalone_misc_settings': {},
|
||||
'standalone_recently_opened': v'[]',
|
||||
@ -75,6 +77,8 @@ is_local_setting = {
|
||||
'max_text_width': True,
|
||||
'override_book_colors': True,
|
||||
'read_mode': 'paged',
|
||||
'scroll_auto_boundary_delay': True,
|
||||
'scroll_stop_boundaries': True,
|
||||
'standalone_font_settings': True,
|
||||
'standalone_misc_settings': True,
|
||||
'standalone_recently_opened': True,
|
||||
|
Loading…
x
Reference in New Issue
Block a user