mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-11 09:13:57 -04:00
Start work on time left display for browser viewer
This commit is contained in:
parent
8dd4d7bb3d
commit
575422bab2
@ -170,6 +170,9 @@ class IframeBoss:
|
|||||||
def gesture_from_margin(self, data):
|
def gesture_from_margin(self, data):
|
||||||
self.handle_gesture(data.gesture)
|
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 on_scroll_to_anchor(self, data):
|
def on_scroll_to_anchor(self, data):
|
||||||
frag = data.frag
|
frag = data.frag
|
||||||
if frag:
|
if frag:
|
||||||
|
@ -56,13 +56,13 @@ def create_page_div(elem):
|
|||||||
_in_paged_mode = False
|
_in_paged_mode = False
|
||||||
def in_paged_mode():
|
def in_paged_mode():
|
||||||
return _in_paged_mode
|
return _in_paged_mode
|
||||||
col_width = screen_width = screen_height = cols_per_screen = gap = col_and_gap = 0
|
col_width = screen_width = screen_height = cols_per_screen = gap = col_and_gap = number_of_cols = 0
|
||||||
is_full_screen_layout = False
|
is_full_screen_layout = False
|
||||||
|
|
||||||
def reset_paged_mode_globals():
|
def reset_paged_mode_globals():
|
||||||
nonlocal _in_paged_mode, col_width, col_and_gap, screen_height, gap, screen_width, is_full_screen_layout, cols_per_screen
|
nonlocal _in_paged_mode, col_width, col_and_gap, screen_height, gap, screen_width, is_full_screen_layout, cols_per_screen, number_of_cols
|
||||||
scroll_viewport.reset_globals()
|
scroll_viewport.reset_globals()
|
||||||
col_width = screen_width = screen_height = cols_per_screen = gap = col_and_gap = 0
|
col_width = screen_width = screen_height = cols_per_screen = gap = col_and_gap = number_of_cols = 0
|
||||||
is_full_screen_layout = _in_paged_mode = False
|
is_full_screen_layout = _in_paged_mode = False
|
||||||
|
|
||||||
def column_at(xpos):
|
def column_at(xpos):
|
||||||
@ -216,12 +216,14 @@ def layout(is_single_page):
|
|||||||
# themselves, unless the container width is an exact multiple, so we check
|
# themselves, unless the container width is an exact multiple, so we check
|
||||||
# for that and manually set the container widths.
|
# for that and manually set the container widths.
|
||||||
def check_column_widths():
|
def check_column_widths():
|
||||||
ncols = (scroll_viewport.paged_content_width() + gap) / col_and_gap
|
nonlocal number_of_cols
|
||||||
|
ncols = number_of_cols = (scroll_viewport.paged_content_width() + gap) / col_and_gap
|
||||||
if ncols is not Math.floor(ncols):
|
if ncols is not Math.floor(ncols):
|
||||||
n = Math.floor(ncols)
|
n = number_of_cols = Math.floor(ncols)
|
||||||
dw = n*col_width + (n-1)*gap
|
dw = n*col_width + (n-1)*gap
|
||||||
data = {'col_width':col_width, 'gap':gap, 'scrollWidth':scroll_viewport.paged_content_width(), 'ncols':ncols, 'desired_width':dw}
|
data = {'col_width':col_width, 'gap':gap, 'scrollWidth':scroll_viewport.paged_content_width(), 'ncols':ncols, 'desired_width':dw}
|
||||||
return data
|
return data
|
||||||
|
|
||||||
data = check_column_widths()
|
data = check_column_widths()
|
||||||
if data:
|
if data:
|
||||||
dw = data.desired_width
|
dw = data.desired_width
|
||||||
@ -459,11 +461,19 @@ def onwheel(evt):
|
|||||||
def scroll_by_page(backward, by_screen):
|
def scroll_by_page(backward, by_screen):
|
||||||
if by_screen:
|
if by_screen:
|
||||||
pos = previous_screen_location() if backward else next_screen_location()
|
pos = previous_screen_location() if backward else next_screen_location()
|
||||||
|
pages = cols_per_screen
|
||||||
else:
|
else:
|
||||||
pos = previous_col_location() if backward else next_col_location()
|
pos = previous_col_location() if backward else next_col_location()
|
||||||
|
pages = 1
|
||||||
if pos is -1:
|
if pos is -1:
|
||||||
|
get_boss().report_human_scroll()
|
||||||
get_boss().send_message('next_spine_item', previous=backward)
|
get_boss().send_message('next_spine_item', previous=backward)
|
||||||
else:
|
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)
|
||||||
|
else:
|
||||||
|
get_boss().report_human_scroll()
|
||||||
scroll_to_xpos(pos)
|
scroll_to_xpos(pos)
|
||||||
|
|
||||||
def onkeydown(evt):
|
def onkeydown(evt):
|
||||||
@ -472,21 +482,25 @@ def onkeydown(evt):
|
|||||||
if key is 'up' or key is 'down':
|
if key is 'up' or key is 'down':
|
||||||
handled = True
|
handled = True
|
||||||
if evt.ctrlKey:
|
if evt.ctrlKey:
|
||||||
|
get_boss().report_human_scroll()
|
||||||
scroll_to_offset(0 if key is 'left' else document_width())
|
scroll_to_offset(0 if key is 'left' else document_width())
|
||||||
else:
|
else:
|
||||||
scroll_by_page(key is 'up', True)
|
scroll_by_page(key is 'up', True)
|
||||||
elif (key is 'left' or key is 'right') and not evt.altKey:
|
elif (key is 'left' or key is 'right') and not evt.altKey:
|
||||||
handled = True
|
handled = True
|
||||||
if evt.ctrlKey:
|
if evt.ctrlKey:
|
||||||
|
get_boss().report_human_scroll()
|
||||||
scroll_to_offset(0 if key is 'left' else document_width())
|
scroll_to_offset(0 if key is 'left' else document_width())
|
||||||
else:
|
else:
|
||||||
scroll_by_page(key is 'left', False)
|
scroll_by_page(key is 'left', False)
|
||||||
elif key is 'home' or key is 'end':
|
elif key is 'home' or key is 'end':
|
||||||
handled = True
|
handled = True
|
||||||
if evt.ctrlKey:
|
if evt.ctrlKey:
|
||||||
|
get_boss().report_human_scroll()
|
||||||
get_boss().send_message('goto_doc_boundary', start=key is 'home')
|
get_boss().send_message('goto_doc_boundary', start=key is 'home')
|
||||||
else:
|
else:
|
||||||
if key is 'home':
|
if key is 'home':
|
||||||
|
get_boss().report_human_scroll()
|
||||||
scroll_to_offset(0)
|
scroll_to_offset(0)
|
||||||
else:
|
else:
|
||||||
scroll_to_offset(document_width())
|
scroll_to_offset(document_width())
|
||||||
|
56
src/pyj/read_book/timers.pyj
Normal file
56
src/pyj/read_book/timers.pyj
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
from __python__ import bound_methods, hash_literals
|
||||||
|
|
||||||
|
THRESHOLD = 5
|
||||||
|
FILTER_THRESHOLD = 25
|
||||||
|
MAX_SAMPLES = 256
|
||||||
|
|
||||||
|
class Timers:
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.reset_read_timer()
|
||||||
|
self.rates = v'[]'
|
||||||
|
self.average = self.stddev = 0
|
||||||
|
|
||||||
|
def start_book(self, book):
|
||||||
|
self.reset_read_timer()
|
||||||
|
self.rates = v'[]'
|
||||||
|
|
||||||
|
def reset_read_timer(self):
|
||||||
|
self.last_scroll_at = None
|
||||||
|
|
||||||
|
def calculate(self):
|
||||||
|
rates = self.rates
|
||||||
|
rlen = rates.length
|
||||||
|
if rlen >= THRESHOLD:
|
||||||
|
avg = 0
|
||||||
|
for v'var i = 0; i < rlen; i++':
|
||||||
|
avg += rates[i]
|
||||||
|
avg /= rlen
|
||||||
|
self.average = avg
|
||||||
|
sq = 0
|
||||||
|
for v'var i = 0; i < rlen; i++':
|
||||||
|
x = rates[i]
|
||||||
|
sq += (x - avg) * (x - avg)
|
||||||
|
self.stddev = Math.sqrt(sq / (rlen - 1))
|
||||||
|
else:
|
||||||
|
self.average = self.stddev = 0
|
||||||
|
|
||||||
|
def on_human_scroll(self, amt_scrolled, is_large_scroll):
|
||||||
|
last_scroll_at = self.last_scroll_at
|
||||||
|
self.last_scroll_at = now = window.performance.now()
|
||||||
|
if last_scroll_at is None:
|
||||||
|
return
|
||||||
|
time_since_last_scroll = now - last_scroll_at
|
||||||
|
if time_since_last_scroll <= 0 or time_since_last_scroll >= 300:
|
||||||
|
return
|
||||||
|
if is_large_scroll and 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:
|
||||||
|
return
|
||||||
|
if self.rates.length >= MAX_SAMPLES:
|
||||||
|
self.rates.shift()
|
||||||
|
self.rates.push(rate)
|
||||||
|
self.calculate()
|
@ -23,6 +23,7 @@ from read_book.prefs.font_size import change_font_size_by
|
|||||||
from read_book.prefs.head_foot import render_head_foot
|
from read_book.prefs.head_foot import render_head_foot
|
||||||
from read_book.resources import load_resources
|
from read_book.resources import load_resources
|
||||||
from read_book.search import SearchOverlay, find_in_spine
|
from read_book.search import SearchOverlay, find_in_spine
|
||||||
|
from read_book.timers import Timers
|
||||||
from read_book.toc import get_current_toc_nodes, update_visible_toc_nodes
|
from read_book.toc import get_current_toc_nodes, update_visible_toc_nodes
|
||||||
from read_book.touch import set_left_margin_handler, set_right_margin_handler
|
from read_book.touch import set_left_margin_handler, set_right_margin_handler
|
||||||
from session import get_device_uuid, get_interface_data
|
from session import get_device_uuid, get_interface_data
|
||||||
@ -116,6 +117,7 @@ class View:
|
|||||||
|
|
||||||
def __init__(self, container, ui):
|
def __init__(self, container, ui):
|
||||||
self.ui = ui
|
self.ui = ui
|
||||||
|
self.timers = Timers()
|
||||||
self.loaded_resources = {}
|
self.loaded_resources = {}
|
||||||
self.current_progress_frac = 0
|
self.current_progress_frac = 0
|
||||||
self.current_toc_node = self.current_toc_toplevel_node = None
|
self.current_toc_node = self.current_toc_toplevel_node = None
|
||||||
@ -163,6 +165,7 @@ class View:
|
|||||||
'request_size': self.on_request_size,
|
'request_size': self.on_request_size,
|
||||||
'show_footnote': self.on_show_footnote,
|
'show_footnote': self.on_show_footnote,
|
||||||
'print': self.on_print,
|
'print': self.on_print,
|
||||||
|
'human_scroll': self.on_human_scroll,
|
||||||
}
|
}
|
||||||
self.iframe_wrapper = IframeWrapper(handlers, document.getElementById(iframe_id), 'read_book.iframe', _('Bootstrapping book reader...'))
|
self.iframe_wrapper = IframeWrapper(handlers, document.getElementById(iframe_id), 'read_book.iframe', _('Bootstrapping book reader...'))
|
||||||
self.search_overlay = SearchOverlay(self)
|
self.search_overlay = SearchOverlay(self)
|
||||||
@ -214,6 +217,16 @@ class View:
|
|||||||
def on_print(self, data):
|
def on_print(self, data):
|
||||||
print(data.string)
|
print(data.string)
|
||||||
|
|
||||||
|
def on_human_scroll(self, data):
|
||||||
|
if data.scrolled_by_frac is None:
|
||||||
|
self.timers.reset_read_timer()
|
||||||
|
else:
|
||||||
|
name = self.currently_showing.name
|
||||||
|
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)
|
||||||
|
|
||||||
def find(self, text, backwards):
|
def find(self, text, backwards):
|
||||||
self.iframe_wrapper.send_message('find', text=text, backwards=backwards, searched_in_spine=False)
|
self.iframe_wrapper.send_message('find', text=text, backwards=backwards, searched_in_spine=False)
|
||||||
|
|
||||||
@ -361,6 +374,7 @@ class View:
|
|||||||
self.content_popup_overlay.iframe_wrapper.reset()
|
self.content_popup_overlay.iframe_wrapper.reset()
|
||||||
self.loaded_resources = {}
|
self.loaded_resources = {}
|
||||||
self.content_popup_overlay.loaded_resources = {}
|
self.content_popup_overlay.loaded_resources = {}
|
||||||
|
self.timers.start_book(book)
|
||||||
self.book = current_book.book = book
|
self.book = current_book.book = book
|
||||||
self.ui.db.update_last_read_time(book)
|
self.ui.db.update_last_read_time(book)
|
||||||
pos = {'replace_history':True}
|
pos = {'replace_history':True}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user