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 dom import ensure_id, set_css
|
||||||
from modals import error_dialog
|
from modals import error_dialog
|
||||||
from read_book.globals import current_book, ui_operations
|
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 read_book.toc import get_border_nodes, get_toc_maps
|
||||||
from widgets import create_button
|
from widgets import create_button
|
||||||
|
|
||||||
@ -50,11 +51,18 @@ def create_goto_panel(current_cfi, book, container, onclick):
|
|||||||
container.appendChild(panel)
|
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_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:
|
if current_cfi:
|
||||||
container.appendChild(E.div(
|
container.lastChild.appendChild(E.div(
|
||||||
style='margin: 1rem; display: flex; align-items: baseline',
|
style='margin: 1rem 0; display: flex; align-items: baseline',
|
||||||
E.input(
|
E.input(
|
||||||
type='text', readonly='readonly', value=_('Currently at: {}').format(current_cfi), name='current_location',
|
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'
|
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(
|
def goto_loc():
|
||||||
style='margin: 1rem;',
|
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(
|
E.div(
|
||||||
style='display: flex; align-items: baseline',
|
style='display: flex; align-items: baseline',
|
||||||
E.label(_('Go to:'), style='margin-right: 1rem'),
|
E.label(_('Go to:'), style='margin-right: 1rem'),
|
||||||
E.input(name='newloc', type='text', style='flex-grow: 10; margin-right: 1rem'), E.span(' '),
|
E.input(name='newloc', type='text', style='flex-grow: 10; margin-right: 1rem', onkeydown=def(ev):
|
||||||
create_button(_('Go'), action=def():
|
if ev.key is 'Enter':
|
||||||
src = document.querySelector(f'#{container_id} [name=newloc]')
|
goto_loc()
|
||||||
if src.value:
|
),
|
||||||
if ui_operations.goto_bookpos(src.value):
|
E.span(' '),
|
||||||
overlay.hide()
|
create_button(_('Go'), action=goto_loc)
|
||||||
else:
|
)
|
||||||
error_dialog(_('No such location'), _(
|
))
|
||||||
'No location {} found').format(src.value))
|
|
||||||
)
|
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):
|
def show_ask_for_location(self):
|
||||||
self.hide_current_panel()
|
self.hide_current_panel()
|
||||||
self.panels.push(SimpleOverlay(
|
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()
|
self.show_current_panel()
|
||||||
|
|
||||||
def show_search(self):
|
def show_search(self):
|
||||||
|
@ -144,14 +144,14 @@ def format_pos(progress_frac, length):
|
|||||||
return f'{pos:.1f} / {pages}'
|
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 {}
|
template = get_session_data().get(which) or {}
|
||||||
field = template[region] or 'empty'
|
field = template[region] or 'empty'
|
||||||
interface_data = get_interface_data()
|
interface_data = get_interface_data()
|
||||||
text = ''
|
text = ''
|
||||||
has_clock = False
|
has_clock = False
|
||||||
if field is 'progress':
|
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 + '%'
|
text = percent + '%'
|
||||||
elif field is 'title':
|
elif field is 'title':
|
||||||
text = metadata.title or _('Untitled')
|
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))
|
text = '{} ({})'.format(format_time_left(chapter_time), format_time_left(book_time))
|
||||||
elif field.startswith('pos-'):
|
elif field.startswith('pos-'):
|
||||||
if field is 'pos-book':
|
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:
|
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:
|
if not text:
|
||||||
text = '\xa0'
|
text = '\xa0'
|
||||||
if text is not div.textContent:
|
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_color_scheme = self.update_color_scheme.bind(self)
|
||||||
ui_operations.update_font_size = self.update_font_size.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_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.delete_book = self.delete_book.bind(self)
|
||||||
ui_operations.focus_iframe = self.focus_iframe.bind(self)
|
ui_operations.focus_iframe = self.focus_iframe.bind(self)
|
||||||
ui_operations.toggle_toc = self.toggle_toc.bind(self)
|
ui_operations.toggle_toc = self.toggle_toc.bind(self)
|
||||||
@ -162,6 +163,9 @@ class ReadUI:
|
|||||||
def goto_bookpos(self, bookpos):
|
def goto_bookpos(self, bookpos):
|
||||||
return self.view.goto_bookpos(bookpos)
|
return self.view.goto_bookpos(bookpos)
|
||||||
|
|
||||||
|
def goto_frac(self, frac):
|
||||||
|
return self.view.goto_frac(frac)
|
||||||
|
|
||||||
def delete_book(self, book, proceed):
|
def delete_book(self, book, proceed):
|
||||||
self.db.delete_book(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')
|
print('Failed to update last read position, AJAX call did not succeed')
|
||||||
)
|
)
|
||||||
|
|
||||||
def update_header_footer(self):
|
@property
|
||||||
sd = get_session_data()
|
def current_position_data(self):
|
||||||
has_clock = False
|
|
||||||
|
|
||||||
if self.book?.manifest:
|
if self.book?.manifest:
|
||||||
book_length = self.book.manifest.spine_length or 0
|
book_length = self.book.manifest.spine_length or 0
|
||||||
name = self.currently_showing.name
|
name = self.currently_showing.name
|
||||||
chapter_length = self.book.manifest.files[name]?.length or 0
|
chapter_length = self.book.manifest.files[name]?.length or 0
|
||||||
else:
|
else:
|
||||||
book_length = chapter_length = 0
|
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}
|
pos = {
|
||||||
book_length *= max(0, 1 - self.current_progress_frac)
|
'progress_frac': self.current_progress_frac,
|
||||||
chapter_length *= max(0, 1 - self.current_file_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)
|
book_time = self.timers.time_for(book_length)
|
||||||
chapter_time = self.timers.time_for(chapter_length)
|
chapter_time = self.timers.time_for(chapter_length)
|
||||||
|
|
||||||
@ -797,9 +807,9 @@ class View:
|
|||||||
nonlocal has_clock
|
nonlocal has_clock
|
||||||
if sd.get(sz_attr, 20) > 5:
|
if sd.get(sz_attr, 20) > 5:
|
||||||
mi = self.book.metadata
|
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)
|
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', 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', 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)
|
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
|
has_clock = hca or hcb or hcc
|
||||||
if textc and not textb and not texta:
|
if textc and not textb and not texta:
|
||||||
# Want right-aligned
|
# Want right-aligned
|
||||||
|
@ -282,6 +282,8 @@ if window is window.top:
|
|||||||
view.focus_iframe()
|
view.focus_iframe()
|
||||||
ui_operations.goto_bookpos = def(cfi):
|
ui_operations.goto_bookpos = def(cfi):
|
||||||
return view.goto_bookpos(cfi)
|
return view.goto_bookpos(cfi)
|
||||||
|
ui_operations.goto_frac = def(frac):
|
||||||
|
return view.goto_frac(frac)
|
||||||
ui_operations.toggle_toc = def():
|
ui_operations.toggle_toc = def():
|
||||||
to_python.toggle_toc()
|
to_python.toggle_toc()
|
||||||
ui_operations.toggle_bookmarks = def():
|
ui_operations.toggle_bookmarks = def():
|
||||||
|
Loading…
x
Reference in New Issue
Block a user