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):
|
def report_human_scroll(amt):
|
||||||
if amt > 0:
|
if abs(amt) > 0:
|
||||||
h = scroll_viewport.height()
|
h = scroll_viewport.height()
|
||||||
is_large_scroll = (amt / h) >= 0.5
|
is_large_scroll = (abs(amt) / h) >= 0.5
|
||||||
if is_large_scroll:
|
if is_large_scroll:
|
||||||
clear_small_scrolls()
|
clear_small_scrolls()
|
||||||
get_boss().report_human_scroll(amt / document_height())
|
get_boss().report_human_scroll(amt / document_height())
|
||||||
@ -180,10 +180,19 @@ def handle_shortcut(sc_name, evt):
|
|||||||
|
|
||||||
|
|
||||||
def layout(is_single_page):
|
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')
|
set_css(document.body, margin='0', border_width='0', padding='0')
|
||||||
line_height.doc_style = window.getComputedStyle(document.body)
|
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():
|
def cancel_scroll():
|
||||||
scroll_animator.stop()
|
scroll_animator.stop()
|
||||||
|
|
||||||
@ -202,16 +211,22 @@ class ScrollAnimator:
|
|||||||
return self.animation_id != None
|
return self.animation_id != None
|
||||||
|
|
||||||
def start(self, direction, auto):
|
def start(self, direction, auto):
|
||||||
|
if self.wait:
|
||||||
|
return
|
||||||
|
|
||||||
now = performance.now()
|
now = performance.now()
|
||||||
self.end_time = now + self.DURATION
|
self.end_time = now + self.DURATION
|
||||||
|
clearTimeout(self.auto_timer)
|
||||||
|
|
||||||
if !self.is_running() or direction != self.direction or auto != self.auto:
|
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.stop()
|
||||||
self.auto = auto
|
self.auto = auto
|
||||||
self.direction = direction
|
self.direction = direction
|
||||||
self.start_time = now
|
self.start_time = now
|
||||||
self.start_offset = window.pageYOffset
|
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)
|
self.animation_id = window.requestAnimationFrame(self.auto_scroll if auto else self.smooth_scroll)
|
||||||
|
|
||||||
def smooth_scroll(self, ts):
|
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
|
scroll_target += Math.trunc(self.direction * progress * duration * line_height() * opts.lines_per_sec_smooth) / 1000
|
||||||
|
|
||||||
window.scrollTo(0, scroll_target)
|
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)
|
self.animation_id = window.requestAnimationFrame(self.smooth_scroll)
|
||||||
|
elif self.paused:
|
||||||
|
self.resume()
|
||||||
else:
|
else:
|
||||||
amt = window.pageYOffset - self.start_offset
|
self.animation_id = None
|
||||||
if abs(amt) < 3 and duration is self.DURATION and is_scroll_end(scroll_target):
|
report_human_scroll(amt)
|
||||||
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)
|
|
||||||
|
|
||||||
def auto_scroll(self, ts):
|
def auto_scroll(self, ts):
|
||||||
elapsed = max(0, ts - self.start_time) # max to account for jitter
|
elapsed = max(0, ts - self.start_time) # max to account for jitter
|
||||||
@ -247,24 +266,45 @@ class ScrollAnimator:
|
|||||||
self.sync(ts)
|
self.sync(ts)
|
||||||
|
|
||||||
if scroll_finished:
|
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:
|
else:
|
||||||
self.animation_id = window.requestAnimationFrame(self.auto_scroll)
|
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):
|
def sync(self, ts):
|
||||||
if self.auto:
|
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_time = ts or performance.now()
|
||||||
self.start_offset = window.pageYOffset
|
self.start_offset = window.pageYOffset
|
||||||
|
else:
|
||||||
|
self.resume()
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self.auto = False
|
self.auto = False
|
||||||
if self.animation_id is not None:
|
if self.animation_id is not None:
|
||||||
window.cancelAnimationFrame(self.animation_id)
|
window.cancelAnimationFrame(self.animation_id)
|
||||||
self.animation_id = None
|
self.animation_id = None
|
||||||
amt = window.pageYOffset - self.start_offset
|
self.report()
|
||||||
if amt > 0:
|
|
||||||
report_human_scroll(amt)
|
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()
|
scroll_animator = ScrollAnimator()
|
||||||
|
|
||||||
|
@ -270,7 +270,6 @@ class IframeBoss:
|
|||||||
self.last_window_width, self.last_window_height = scroll_viewport.width(), scroll_viewport.height()
|
self.last_window_width, self.last_window_height = scroll_viewport.width(), scroll_viewport.height()
|
||||||
apply_settings()
|
apply_settings()
|
||||||
fix_fullscreen_svg_images()
|
fix_fullscreen_svg_images()
|
||||||
self.do_layout(self.is_titlepage)
|
|
||||||
if self.mathjax:
|
if self.mathjax:
|
||||||
return apply_mathjax(self.mathjax, self.book.manifest.link_uid, self.content_loaded_stage2)
|
return apply_mathjax(self.mathjax, self.book.manifest.link_uid, self.content_loaded_stage2)
|
||||||
# window.setTimeout(self.content_loaded_stage2, 1000)
|
# window.setTimeout(self.content_loaded_stage2, 1000)
|
||||||
@ -307,6 +306,7 @@ class IframeBoss:
|
|||||||
if si:
|
if si:
|
||||||
self.length_before += files[si]?.length or 0
|
self.length_before += files[si]?.length or 0
|
||||||
self.onscroll()
|
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.send_message('content_loaded', progress_frac=self.calculate_progress_frac(), file_progress_frac=progress_frac())
|
||||||
self.last_cfi = None
|
self.last_cfi = None
|
||||||
window.setTimeout(self.update_cfi, 0)
|
window.setTimeout(self.update_cfi, 0)
|
||||||
|
@ -16,6 +16,9 @@ CONTAINER = unique_id('standalone-scrolling-settings')
|
|||||||
MIN_SCROLL_SPEED_AUTO = 0.25
|
MIN_SCROLL_SPEED_AUTO = 0.25
|
||||||
MAX_SCROLL_SPEED_AUTO = 5
|
MAX_SCROLL_SPEED_AUTO = 5
|
||||||
|
|
||||||
|
MIN_SCROLL_AUTO_DELAY = 0
|
||||||
|
MAX_SCROLL_AUTO_DELAY = 10
|
||||||
|
|
||||||
MIN_SCROLL_SPEED_SMOOTH = 10
|
MIN_SCROLL_SPEED_SMOOTH = 10
|
||||||
MAX_SCROLL_SPEED_SMOOTH = 50
|
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)
|
ans = E.input(type='number', name=name, id=name)
|
||||||
for key, val in Object.entries(kwargs):
|
for key, val in Object.entries(kwargs):
|
||||||
ans[key] = val
|
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
|
return E.label("for"=name, text), ans
|
||||||
|
|
||||||
container.appendChild(E.div(style='margin-top:1ex', _('Control how mouse based scrolling works in paged mode')))
|
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.hr())
|
||||||
container.appendChild(E.div(style='margin-top:1ex', _('Control how smooth scrolling works in flow mode')))
|
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(
|
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(
|
*spinner(
|
||||||
'lines_per_sec_smooth',
|
'lines_per_sec_smooth',
|
||||||
_('Smooth scrolling speed in lines/sec'),
|
_('Smooth scrolling speed in lines/sec'),
|
||||||
@ -83,6 +90,13 @@ def create_scrolling_panel(container, apply_func, cancel_func):
|
|||||||
step=MIN_SCROLL_SPEED_AUTO,
|
step=MIN_SCROLL_SPEED_AUTO,
|
||||||
min=MIN_SCROLL_SPEED_AUTO,
|
min=MIN_SCROLL_SPEED_AUTO,
|
||||||
max=MAX_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.margin_right = max(0, settings.margin_right)
|
||||||
opts.override_book_colors = settings.override_book_colors
|
opts.override_book_colors = settings.override_book_colors
|
||||||
opts.paged_wheel_scrolls_by_screen = v'!!settings.paged_wheel_scrolls_by_screen'
|
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
|
opts.user_stylesheet = settings.user_stylesheet
|
||||||
|
|
||||||
update_settings()
|
update_settings()
|
||||||
|
@ -712,6 +712,8 @@ class View:
|
|||||||
'paged_wheel_scrolls_by_screen': sd.get('paged_wheel_scrolls_by_screen'),
|
'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_auto': sd.get('lines_per_sec_auto'),
|
||||||
'lines_per_sec_smooth': sd.get('lines_per_sec_smooth'),
|
'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):
|
def show_name(self, name, initial_position=None):
|
||||||
|
@ -49,6 +49,8 @@ defaults = {
|
|||||||
'paged_margin_clicks_scroll_by_screen': True,
|
'paged_margin_clicks_scroll_by_screen': True,
|
||||||
'paged_wheel_scrolls_by_screen': False,
|
'paged_wheel_scrolls_by_screen': False,
|
||||||
'read_mode': 'paged',
|
'read_mode': 'paged',
|
||||||
|
'scroll_auto_boundary_delay': 5,
|
||||||
|
'scroll_stop_boundaries': False,
|
||||||
'standalone_font_settings': {},
|
'standalone_font_settings': {},
|
||||||
'standalone_misc_settings': {},
|
'standalone_misc_settings': {},
|
||||||
'standalone_recently_opened': v'[]',
|
'standalone_recently_opened': v'[]',
|
||||||
@ -75,6 +77,8 @@ is_local_setting = {
|
|||||||
'max_text_width': True,
|
'max_text_width': True,
|
||||||
'override_book_colors': True,
|
'override_book_colors': True,
|
||||||
'read_mode': 'paged',
|
'read_mode': 'paged',
|
||||||
|
'scroll_auto_boundary_delay': True,
|
||||||
|
'scroll_stop_boundaries': True,
|
||||||
'standalone_font_settings': True,
|
'standalone_font_settings': True,
|
||||||
'standalone_misc_settings': True,
|
'standalone_misc_settings': True,
|
||||||
'standalone_recently_opened': True,
|
'standalone_recently_opened': True,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user