diff --git a/src/pyj/read_book/flow_mode.pyj b/src/pyj/read_book/flow_mode.pyj index 6660bd9a31..085e918e2c 100644 --- a/src/pyj/read_book/flow_mode.pyj +++ b/src/pyj/read_book/flow_mode.pyj @@ -14,19 +14,72 @@ from utils import document_height, document_width, viewport_to_document def flow_to_scroll_fraction(frac): scroll_viewport.scroll_to(0, document_height() * frac) + +small_scroll_events = v'[]' + + +def clear_small_scrolls(): + nonlocal small_scroll_events + small_scroll_events = v'[]' + + +def dispatch_small_scrolls(): + if small_scroll_events.length: + now = window.performance.now() + if now - small_scroll_events[-1].time <= 2000: + window.setTimeout(dispatch_small_scrolls, 100) + return + amt = 0 + for x in small_scroll_events: + amt += x.amt + clear_small_scrolls() + get_boss().report_human_scroll(amt / document_height()) + + +def add_small_scroll(amt): + small_scroll_events.push({'amt': amt, 'time': window.performance.now()}) + window.setTimeout(dispatch_small_scrolls, 100) + + +def report_human_scroll(amt): + if amt > 0: + h = scroll_viewport.height() + is_large_scroll = (amt / h) >= 0.5 + if is_large_scroll: + clear_small_scrolls() + get_boss().report_human_scroll(amt / document_height()) + else: + add_small_scroll(amt) + else: + clear_small_scrolls() + + +def _check_for_scroll_end(func, obj, args, report): + before = window.pageYOffset + func.apply(obj, args) + if window.pageYOffset is before: + get_boss().send_message('next_spine_item', previous=arguments[0] < 0) + return False + if report: + report_human_scroll(window.pageYOffset - before) + return True + + def check_for_scroll_end(func): return def (): - before = window.pageYOffset - func.apply(this, arguments) - if window.pageYOffset is before: - get_boss().send_message('next_spine_item', previous=arguments[0] < 0) - return False - return True + return _check_for_scroll_end(func, this, arguments, False) -@check_for_scroll_end + +def check_for_scroll_end_and_report(func): + return def (): + return _check_for_scroll_end(func, this, arguments, True) + + +@check_for_scroll_end_and_report def scroll_by(y): window.scrollBy(0, y) + def flow_onwheel(evt): dx = dy = 0 if evt.deltaY: @@ -61,15 +114,19 @@ def smooth_y_scroll(up): smooth_y_data.up = up do_y_scroll() + @check_for_scroll_end def goto_boundary(y): scroll_viewport.scroll_to(window.pageXOffset, 0) + get_boss().report_human_scroll() -@check_for_scroll_end + +@check_for_scroll_end_and_report def scroll_by_page(backward): h = scroll_viewport.height() - 10 window.scrollBy(0, -h if backward else h) + def flow_onkeydown(evt): handled = False key = get_key(evt) @@ -87,6 +144,8 @@ def flow_onkeydown(evt): window.scrollBy(-15 if key is 'left' else 15, 0) elif key is 'home' or key is 'end': handled = True + get_boss().report_human_scroll() + clear_small_scrolls() if evt.ctrlKey: get_boss().send_message('goto_doc_boundary', start=key is 'home') else: @@ -100,9 +159,11 @@ def flow_onkeydown(evt): if handled: evt.preventDefault() + def layout(is_single_page): set_css(document.body, margin='0', border_width='0', padding='0') + class FlickAnimator: SPEED_FACTOR = 0.04 diff --git a/src/pyj/read_book/iframe.pyj b/src/pyj/read_book/iframe.pyj index 9d12f35fb4..c12b0e0796 100644 --- a/src/pyj/read_book/iframe.pyj +++ b/src/pyj/read_book/iframe.pyj @@ -170,8 +170,8 @@ class IframeBoss: def gesture_from_margin(self, data): self.handle_gesture(data.gesture) - def report_human_scroll(self, scrolled_by_frac, is_large_scroll): - self.send_message('human_scroll', scrolled_by_frac=scrolled_by_frac or None, is_large_scroll=v'!!is_large_scroll') + def report_human_scroll(self, scrolled_by_frac): + self.send_message('human_scroll', scrolled_by_frac=scrolled_by_frac or None) def on_scroll_to_anchor(self, data): frag = data.frag diff --git a/src/pyj/read_book/paged_mode.pyj b/src/pyj/read_book/paged_mode.pyj index 874c58fd13..f024816b2f 100644 --- a/src/pyj/read_book/paged_mode.pyj +++ b/src/pyj/read_book/paged_mode.pyj @@ -472,7 +472,7 @@ def scroll_by_page(backward, by_screen): else: if not backward: scrolled_frac = (pages / number_of_cols) if number_of_cols > 0 else 0 - get_boss().report_human_scroll(scrolled_frac, True) + get_boss().report_human_scroll(scrolled_frac) else: get_boss().report_human_scroll() scroll_to_xpos(pos) diff --git a/src/pyj/read_book/timers.pyj b/src/pyj/read_book/timers.pyj index c41b294486..728e9121c8 100644 --- a/src/pyj/read_book/timers.pyj +++ b/src/pyj/read_book/timers.pyj @@ -37,7 +37,7 @@ class Timers: else: self.average = self.stddev = 0 - def on_human_scroll(self, amt_scrolled, is_large_scroll): + def on_human_scroll(self, amt_scrolled): last_scroll_at = self.last_scroll_at self.last_scroll_at = now = window.performance.now() if last_scroll_at is None: @@ -45,7 +45,7 @@ class Timers: time_since_last_scroll = (now - last_scroll_at) / 1000 if time_since_last_scroll <= 0 or time_since_last_scroll >= 300: return - if is_large_scroll and time_since_last_scroll < 2: + if time_since_last_scroll < 2: return rate = amt_scrolled / time_since_last_scroll if self.rates.length >= FILTER_THRESHOLD and Math.abs(rate - self.average) > 2 * self.stddev: diff --git a/src/pyj/read_book/view.pyj b/src/pyj/read_book/view.pyj index a57f5833b8..3cded9fdcf 100644 --- a/src/pyj/read_book/view.pyj +++ b/src/pyj/read_book/view.pyj @@ -225,7 +225,7 @@ class View: length = self.book.manifest.files[name]?.length if length: amt_scrolled = data.scrolled_by_frac * length - self.timers.on_human_scroll(amt_scrolled, data.is_large_scroll) + self.timers.on_human_scroll(amt_scrolled) def find(self, text, backwards): self.iframe_wrapper.send_message('find', text=text, backwards=backwards, searched_in_spine=False)