mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add auto-scroll with keyboard shortcut
This commit is contained in:
parent
0229c684f3
commit
e028bbb3f6
@ -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
|
||||
|
@ -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 ''
|
||||
|
@ -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():
|
||||
|
@ -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)
|
||||
|
@ -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'),
|
||||
),
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user