Add auto-scroll with keyboard shortcut

This commit is contained in:
Michael Ziminsky (Z) 2019-12-24 00:29:01 -07:00
parent 0229c684f3
commit e028bbb3f6
7 changed files with 79 additions and 15 deletions

View File

@ -63,8 +63,11 @@ last_change_spine_item_request = {}
def _check_for_scroll_end(func, obj, args, report):
before = window.pageYOffset
func.apply(obj, args)
now = performance.now()
scroll_animator.sync(now)
if window.pageYOffset is before:
now = Date.now()
csi = current_spine_item()
if last_change_spine_item_request.name is csi.name and now - last_change_spine_item_request.at < 2000:
return False
@ -128,10 +131,10 @@ def scroll_by_page(direction):
def handle_shortcut(sc_name, evt):
if sc_name is 'down':
scroll_animator.start(DIRECTION.Down)
scroll_animator.start(DIRECTION.Down, False)
return True
if sc_name is 'up':
scroll_animator.start(DIRECTION.Up)
scroll_animator.start(DIRECTION.Up, False)
return True
if sc_name is 'start_of_file':
goto_boundary(-1)
@ -157,13 +160,29 @@ def handle_shortcut(sc_name, evt):
if sc_name is 'pagedown':
scroll_by_page(1)
return True
if sc_name is 'toggle_autoscroll':
if scroll_animator.auto and scroll_animator.is_running():
cancel_scroll()
else:
scroll_animator.start(DIRECTION.Down, True)
return True
if sc_name.startsWith('scrollspeed_'):
scroll_animator.sync()
return False
def layout(is_single_page):
cancel_scroll()
set_css(document.body, margin='0', border_width='0', padding='0')
line_height.doc_style = window.getComputedStyle(document.body)
def cancel_scroll():
scroll_animator.stop()
def is_scroll_end(pos):
return !(0 <= pos <= document_height() - window.innerHeight)
DIRECTION = {'Up': -1, 'Down': 1}
class ScrollAnimator:
@ -171,17 +190,23 @@ class ScrollAnimator:
def __init__(self):
self.animation_id = None
self.auto = False
def start(self, direction):
def is_running(self):
return self.animation_id != None
def start(self, direction, auto):
now = performance.now()
self.end_time = now + self.DURATION
if self.animation_id is None or direction != self.direction:
if !self.is_running() or direction != self.direction or auto != self.auto:
self.paused = self.direction if self.auto and not auto else False
self.stop()
self.auto = auto
self.direction = direction
self.start_time = now
self.start_offset = window.pageYOffset
self.animation_id = window.requestAnimationFrame(self.smooth_scroll)
self.animation_id = window.requestAnimationFrame(self.auto_scroll if auto else self.smooth_scroll)
def smooth_scroll(self, ts):
duration = (self.end_time - self.start_time)
@ -194,14 +219,40 @@ class ScrollAnimator:
if progress < 1:
self.animation_id = window.requestAnimationFrame(self.smooth_scroll)
else:
self.animation_id = None
amt = window.pageYOffset - self.start_offset
if abs(amt) < 3 and duration is self.DURATION and !(0 <= scroll_target <= document_height() - window.innerHeight):
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)
def auto_scroll(self, ts):
elapsed = max(0, ts - self.start_time) # max to account for jitter
scroll_target = self.start_offset
scroll_target += Math.trunc(self.direction * elapsed * line_height() * opts.lines_per_sec_auto) / 1000
window.scrollTo(0, scroll_target)
scroll_finished = is_scroll_end(scroll_target)
# report every second
if elapsed >= 1000:
self.sync(ts)
if scroll_finished:
self.stop()
else:
self.animation_id = window.requestAnimationFrame(self.auto_scroll)
def sync(self, ts):
if self.auto:
report_human_scroll(window.pageYOffset - self.start_offset)
self.start_time = ts or performance.now()
self.start_offset = window.pageYOffset
def stop(self):
self.auto = False
if self.animation_id is not None:
window.cancelAnimationFrame(self.animation_id)
self.animation_id = None

View File

@ -12,7 +12,7 @@ from read_book.extract import get_elements
from read_book.flow_mode import (
anchor_funcs as flow_anchor_funcs, flow_onwheel, flow_to_scroll_fraction,
handle_gesture as flow_handle_gesture, handle_shortcut as flow_handle_shortcut,
layout as flow_layout, scroll_by_page as flow_scroll_by_page
layout as flow_layout, scroll_by_page as flow_scroll_by_page, cancel_scroll
)
from read_book.footnotes import is_footnote_link
from read_book.globals import (
@ -108,6 +108,7 @@ class IframeBoss:
'set_reference_mode': self.set_reference_mode,
'wheel_from_margin': self.wheel_from_margin,
'window_size': self.received_window_size,
'overlay_shown': cancel_scroll,
}
self.comm = IframeClient(handlers)
self.last_window_ypos = 0
@ -245,8 +246,8 @@ class IframeBoss:
apply_font_size()
def change_scroll_speed(self, data):
if data.lines_per_sec_smooth?:
opts.lines_per_sec_smooth = data.lines_per_sec_smooth
if data.lines_per_sec_auto?:
opts.lines_per_sec_auto = data.lines_per_sec_auto
def change_stylesheet(self, data):
opts.user_stylesheet = data.sheet or ''

View File

@ -12,7 +12,7 @@ from session import defaults
CONTAINER = unique_id('standalone-scrolling-settings')
MIN_SCROLL_SPEED = 0.5
MAX_SCROLL_SPEED = 50
MAX_SCROLL_SPEED = 5
def restore_defaults():

View File

@ -17,6 +17,7 @@ def update_settings(settings):
opts.cover_preserve_aspect_ratio = v'!!settings.cover_preserve_aspect_ratio'
opts.hide_tooltips = settings.hide_tooltips
opts.is_dark_theme = v'!!settings.is_dark_theme'
opts.lines_per_sec_auto = settings.lines_per_sec_auto
opts.lines_per_sec_smooth = settings.lines_per_sec_smooth
opts.margin_left = max(0, settings.margin_left)
opts.margin_right = max(0, settings.margin_right)

View File

@ -270,16 +270,22 @@ def shortcuts_definition():
_('Go to a specified book location or position'),
),
'toggle_autoscroll': desc(
"Ctrl+ ",
'scroll',
_('Toggle auto-scroll'),
),
'scrollspeed_increase': desc(
"Alt+ArrowUp",
'scroll',
_('Smooth scroll faster'),
_('Auto scroll faster'),
),
'scrollspeed_decrease': desc(
"Alt+ArrowDown",
'scroll',
_('Smooth scroll slower'),
_('Auto scroll slower'),
),
}

View File

@ -339,6 +339,8 @@ class View:
def overlay_visibility_changed(self, visible):
if self.iframe_wrapper.send_message:
if visible:
self.iframe_wrapper.send_message('overlay_shown')
self.iframe_wrapper.send_message('set_forward_keypresses', forward=v'!!visible')
if ui_operations.overlay_visibility_changed:
ui_operations.overlay_visibility_changed(visible)
@ -708,6 +710,7 @@ class View:
'hide_tooltips': sd.get('hide_tooltips'),
'cover_preserve_aspect_ratio': sd.get('cover_preserve_aspect_ratio'),
'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'),
}
@ -1000,7 +1003,7 @@ class View:
self.iframe_wrapper.send_message('change_font_size', base_font_size=get_session_data().get('base_font_size'))
def update_scroll_speed(self, amt):
self.iframe_wrapper.send_message('change_scroll_speed', lines_per_sec_smooth=change_scroll_speed(amt))
self.iframe_wrapper.send_message('change_scroll_speed', lines_per_sec_auto=change_scroll_speed(amt))
def update_color_scheme(self):
cs = self.get_color_scheme(True)

View File

@ -37,6 +37,7 @@ defaults = {
'header': {},
'hide_tooltips': False,
'keyboard_shortcuts': {},
'lines_per_sec_auto': 1,
'lines_per_sec_smooth': 30,
'margin_bottom': 20,
'margin_left': 20,
@ -64,6 +65,7 @@ is_local_setting = {
'columns_per_screen': True,
'controls_help_shown_count': True,
'current_color_scheme': True,
'lines_per_sec_auto': True,
'lines_per_sec_smooth': True,
'margin_bottom': True,
'margin_left': True,