mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Viewer: Allow jumping to a book position, in addition to a CFI location via Go to->Location
This commit is contained in:
parent
1cdface5ad
commit
3faa52ee58
@ -9,6 +9,7 @@ from book_list.item_list import build_list, create_item
|
||||
from dom import ensure_id, set_css
|
||||
from modals import error_dialog
|
||||
from read_book.globals import current_book, ui_operations
|
||||
from read_book.prefs.head_foot import format_pos
|
||||
from read_book.toc import get_border_nodes, get_toc_maps
|
||||
from widgets import create_button
|
||||
|
||||
@ -50,11 +51,18 @@ def create_goto_panel(current_cfi, book, container, onclick):
|
||||
container.appendChild(panel)
|
||||
|
||||
|
||||
def create_location_overlay(current_cfi, overlay, container):
|
||||
def create_location_overlay(current_position_data, overlay, container):
|
||||
container_id = ensure_id(container)
|
||||
container.appendChild(E.div(style='margin: 0 1rem'))
|
||||
container = container.lastChild
|
||||
container.appendChild(E.div(
|
||||
style='padding-top: 1rem',
|
||||
E.h4(_('Using book location (most accurate)'))
|
||||
))
|
||||
current_cfi = current_position_data.cfi
|
||||
if current_cfi:
|
||||
container.appendChild(E.div(
|
||||
style='margin: 1rem; display: flex; align-items: baseline',
|
||||
container.lastChild.appendChild(E.div(
|
||||
style='margin: 1rem 0; display: flex; align-items: baseline',
|
||||
E.input(
|
||||
type='text', readonly='readonly', value=_('Currently at: {}').format(current_cfi), name='current_location',
|
||||
style='border-width: 0; background-color: transparent; outline: none; flex-grow: 10; font-family: inherit'
|
||||
@ -73,20 +81,53 @@ def create_location_overlay(current_cfi, overlay, container):
|
||||
)
|
||||
))
|
||||
|
||||
container.appendChild(E.div(
|
||||
style='margin: 1rem;',
|
||||
def goto_loc():
|
||||
src = document.querySelector(f'#{container_id} [name=newloc]')
|
||||
if src.value:
|
||||
if ui_operations.goto_bookpos(src.value):
|
||||
overlay.hide()
|
||||
else:
|
||||
error_dialog(_('No such location'), _(
|
||||
'No location {} found').format(src.value))
|
||||
|
||||
container.lastChild.appendChild(E.div(
|
||||
style='margin: 1rem 0;',
|
||||
E.div(
|
||||
style='display: flex; align-items: baseline',
|
||||
E.label(_('Go to:'), style='margin-right: 1rem'),
|
||||
E.input(name='newloc', type='text', style='flex-grow: 10; margin-right: 1rem'), E.span(' '),
|
||||
create_button(_('Go'), action=def():
|
||||
src = document.querySelector(f'#{container_id} [name=newloc]')
|
||||
if src.value:
|
||||
if ui_operations.goto_bookpos(src.value):
|
||||
overlay.hide()
|
||||
else:
|
||||
error_dialog(_('No such location'), _(
|
||||
'No location {} found').format(src.value))
|
||||
)
|
||||
E.input(name='newloc', type='text', style='flex-grow: 10; margin-right: 1rem', onkeydown=def(ev):
|
||||
if ev.key is 'Enter':
|
||||
goto_loc()
|
||||
),
|
||||
E.span(' '),
|
||||
create_button(_('Go'), action=goto_loc)
|
||||
)
|
||||
))
|
||||
|
||||
container.appendChild(E.div(
|
||||
style='margin-top: 1rem; border-top: solid 1px; padding-top: 1rem',
|
||||
E.h4(_('Using book position'))
|
||||
))
|
||||
if current_position_data.book_length > 0:
|
||||
container.lastChild.appendChild(
|
||||
E.div(style='margin: 1rem 0', _('Currently at: {}').format(
|
||||
format_pos(current_position_data.progress_frac, current_position_data.book_length))))
|
||||
|
||||
def goto_pos():
|
||||
src = document.querySelector(f'#{container_id} [name=newpos]')
|
||||
val = max(0, min(1000 * float(src.value) / current_position_data.book_length, 1))
|
||||
ui_operations.goto_frac(val)
|
||||
overlay.hide()
|
||||
|
||||
container.lastChild.appendChild(E.div(
|
||||
style='margin: 1rem 0;',
|
||||
E.div(
|
||||
style='display: flex; align-items: baseline',
|
||||
E.label(_('Go to:'), style='margin-right: 1rem'),
|
||||
E.input(name='newpos', type='number', min='0', max=str(current_position_data.book_length), step='0.1', style='flex-grow: 10; margin-right: 1rem', onkeydown=def(ev):
|
||||
if ev.key is 'Enter':
|
||||
goto_pos()
|
||||
), E.span(' '),
|
||||
create_button(_('Go'), action=goto_pos)
|
||||
)
|
||||
))
|
||||
|
@ -621,7 +621,7 @@ class Overlay:
|
||||
def show_ask_for_location(self):
|
||||
self.hide_current_panel()
|
||||
self.panels.push(SimpleOverlay(
|
||||
self, create_location_overlay.bind(None, self.view.currently_showing.bookpos), _('Go to location…')))
|
||||
self, create_location_overlay.bind(None, self.view.current_position_data), _('Go to location…')))
|
||||
self.show_current_panel()
|
||||
|
||||
def show_search(self):
|
||||
|
@ -144,14 +144,14 @@ def format_pos(progress_frac, length):
|
||||
return f'{pos:.1f} / {pages}'
|
||||
|
||||
|
||||
def render_head_foot(div, which, region, progress_frac, metadata, current_toc_node, current_toc_toplevel_node, book_time, chapter_time, pos):
|
||||
def render_head_foot(div, which, region, metadata, current_toc_node, current_toc_toplevel_node, book_time, chapter_time, pos):
|
||||
template = get_session_data().get(which) or {}
|
||||
field = template[region] or 'empty'
|
||||
interface_data = get_interface_data()
|
||||
text = ''
|
||||
has_clock = False
|
||||
if field is 'progress':
|
||||
percent = min(100, max(Math.round(progress_frac * 100), 0))
|
||||
percent = min(100, max(Math.round(pos.progress_frac * 100), 0))
|
||||
text = percent + '%'
|
||||
elif field is 'title':
|
||||
text = metadata.title or _('Untitled')
|
||||
@ -182,9 +182,9 @@ def render_head_foot(div, which, region, progress_frac, metadata, current_toc_no
|
||||
text = '{} ({})'.format(format_time_left(chapter_time), format_time_left(book_time))
|
||||
elif field.startswith('pos-'):
|
||||
if field is 'pos-book':
|
||||
text = format_pos(pos.current_progress_frac, pos.book_length)
|
||||
text = format_pos(pos.progress_frac, pos.book_length)
|
||||
else:
|
||||
text = format_pos(pos.current_file_progress_frac, pos.chapter_length)
|
||||
text = format_pos(pos.file_progress_frac, pos.chapter_length)
|
||||
if not text:
|
||||
text = '\xa0'
|
||||
if text is not div.textContent:
|
||||
|
@ -66,6 +66,7 @@ class ReadUI:
|
||||
ui_operations.update_color_scheme = self.update_color_scheme.bind(self)
|
||||
ui_operations.update_font_size = self.update_font_size.bind(self)
|
||||
ui_operations.goto_bookpos = self.goto_bookpos.bind(self)
|
||||
ui_operations.goto_frac = self.goto_frac.bind(self)
|
||||
ui_operations.delete_book = self.delete_book.bind(self)
|
||||
ui_operations.focus_iframe = self.focus_iframe.bind(self)
|
||||
ui_operations.toggle_toc = self.toggle_toc.bind(self)
|
||||
@ -162,6 +163,9 @@ class ReadUI:
|
||||
def goto_bookpos(self, bookpos):
|
||||
return self.view.goto_bookpos(bookpos)
|
||||
|
||||
def goto_frac(self, frac):
|
||||
return self.view.goto_frac(frac)
|
||||
|
||||
def delete_book(self, book, proceed):
|
||||
self.db.delete_book(book, proceed)
|
||||
|
||||
|
@ -777,19 +777,29 @@ class View:
|
||||
print('Failed to update last read position, AJAX call did not succeed')
|
||||
)
|
||||
|
||||
def update_header_footer(self):
|
||||
sd = get_session_data()
|
||||
has_clock = False
|
||||
|
||||
@property
|
||||
def current_position_data(self):
|
||||
if self.book?.manifest:
|
||||
book_length = self.book.manifest.spine_length or 0
|
||||
name = self.currently_showing.name
|
||||
chapter_length = self.book.manifest.files[name]?.length or 0
|
||||
else:
|
||||
book_length = chapter_length = 0
|
||||
pos = {'current_progress_frac': self.current_progress_frac, 'book_length': book_length, 'chapter_length': chapter_length, 'current_file_progress_frac': self.current_file_progress_frac}
|
||||
book_length *= max(0, 1 - self.current_progress_frac)
|
||||
chapter_length *= max(0, 1 - self.current_file_progress_frac)
|
||||
pos = {
|
||||
'progress_frac': self.current_progress_frac,
|
||||
'book_length': book_length, 'chapter_length': chapter_length,
|
||||
'file_progress_frac': self.current_file_progress_frac,
|
||||
'cfi': self.currently_showing?.bookpos
|
||||
}
|
||||
return pos
|
||||
|
||||
def update_header_footer(self):
|
||||
sd = get_session_data()
|
||||
has_clock = False
|
||||
pos = self.current_position_data
|
||||
|
||||
book_length = pos.book_length * max(0, 1 - pos.progress_frac)
|
||||
chapter_length = pos.chapter_length * max(0, 1 - pos.file_progress_frac)
|
||||
book_time = self.timers.time_for(book_length)
|
||||
chapter_time = self.timers.time_for(chapter_length)
|
||||
|
||||
@ -797,9 +807,9 @@ class View:
|
||||
nonlocal has_clock
|
||||
if sd.get(sz_attr, 20) > 5:
|
||||
mi = self.book.metadata
|
||||
texta, hca = render_head_foot(div.firstChild, name, 'left', self.current_progress_frac, mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time, pos)
|
||||
textb, hcb = render_head_foot(div.firstChild.nextSibling, name, 'middle', self.current_progress_frac, mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time, pos)
|
||||
textc, hcc = render_head_foot(div.lastChild, name, 'right', self.current_progress_frac, mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time, pos)
|
||||
texta, hca = render_head_foot(div.firstChild, name, 'left', mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time, pos)
|
||||
textb, hcb = render_head_foot(div.firstChild.nextSibling, name, 'middle', mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time, pos)
|
||||
textc, hcc = render_head_foot(div.lastChild, name, 'right', mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time, pos)
|
||||
has_clock = hca or hcb or hcc
|
||||
if textc and not textb and not texta:
|
||||
# Want right-aligned
|
||||
|
@ -282,6 +282,8 @@ if window is window.top:
|
||||
view.focus_iframe()
|
||||
ui_operations.goto_bookpos = def(cfi):
|
||||
return view.goto_bookpos(cfi)
|
||||
ui_operations.goto_frac = def(frac):
|
||||
return view.goto_frac(frac)
|
||||
ui_operations.toggle_toc = def():
|
||||
to_python.toggle_toc()
|
||||
ui_operations.toggle_bookmarks = def():
|
||||
|
Loading…
x
Reference in New Issue
Block a user