mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Implement Go to location (with CFI)
This commit is contained in:
parent
cd71b471e1
commit
031b14fc78
@ -78,4 +78,5 @@ ui_operations = {
|
|||||||
'forward_gesture': None,
|
'forward_gesture': None,
|
||||||
'update_color_scheme': None,
|
'update_color_scheme': None,
|
||||||
'update_font_size': None,
|
'update_font_size': None,
|
||||||
|
'goto_bookpos': None,
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ from read_book.toc import get_toc_maps, get_border_nodes
|
|||||||
from read_book.globals import current_book
|
from read_book.globals import current_book
|
||||||
|
|
||||||
|
|
||||||
def create_goto_list(onclick):
|
def create_goto_list(onclick, current_cfi):
|
||||||
ans = E.div()
|
ans = E.div()
|
||||||
items = v'[]'
|
items = v'[]'
|
||||||
landmarks = current_book().manifest.landmarks
|
landmarks = current_book().manifest.landmarks
|
||||||
@ -25,6 +25,7 @@ def create_goto_list(onclick):
|
|||||||
items.push(create_item(_('Document start'), action=onclick.bind(None, def(view): view.goto_doc_boundary(True);)))
|
items.push(create_item(_('Document start'), action=onclick.bind(None, def(view): view.goto_doc_boundary(True);)))
|
||||||
items.push(create_item(_('Document end'), action=onclick.bind(None, def(view): view.goto_doc_boundary(False);)))
|
items.push(create_item(_('Document end'), action=onclick.bind(None, def(view): view.goto_doc_boundary(False);)))
|
||||||
items.push(create_item(_('Metadata'), subtitle=_('Details about this book'), action=onclick.bind(None, def(view): view.show_book_metadata();)))
|
items.push(create_item(_('Metadata'), subtitle=_('Details about this book'), action=onclick.bind(None, def(view): view.show_book_metadata();)))
|
||||||
|
items.push(create_item(_('Location'), subtitle=current_cfi or None, action=onclick.bind(None, def(view): view.ask_for_location();)))
|
||||||
for l in landmarks:
|
for l in landmarks:
|
||||||
items.push(create_item(l.title, action=onclick.bind(None, l.dest, l.frag)))
|
items.push(create_item(l.title, action=onclick.bind(None, l.dest, l.frag)))
|
||||||
build_list(ans, items)
|
build_list(ans, items)
|
||||||
@ -38,8 +39,8 @@ def get_next_section(forward):
|
|||||||
return after if forward else before
|
return after if forward else before
|
||||||
|
|
||||||
|
|
||||||
def create_goto_panel(book, container, onclick):
|
def create_goto_panel(current_cfi, book, container, onclick):
|
||||||
panel = create_goto_list(onclick)
|
panel = create_goto_list(onclick, current_cfi)
|
||||||
set_css(container, display='flex', flex_direction='column')
|
set_css(container, display='flex', flex_direction='column')
|
||||||
set_css(panel, flex_grow='10')
|
set_css(panel, flex_grow='10')
|
||||||
container.appendChild(panel)
|
container.appendChild(panel)
|
||||||
|
@ -520,7 +520,7 @@ class Overlay:
|
|||||||
|
|
||||||
def show_goto(self):
|
def show_goto(self):
|
||||||
self.hide_current_panel()
|
self.hide_current_panel()
|
||||||
self.panels.push(TOCOverlay(self, create_goto_panel, _('Go to…')))
|
self.panels.push(TOCOverlay(self, create_goto_panel.bind(None, self.view.currently_showing.bookpos), _('Go to…')))
|
||||||
self.show_current_panel()
|
self.show_current_panel()
|
||||||
|
|
||||||
def show_search(self):
|
def show_search(self):
|
||||||
|
@ -65,6 +65,7 @@ class ReadUI:
|
|||||||
ui_operations.forward_gesture = self.forward_gesture.bind(self)
|
ui_operations.forward_gesture = self.forward_gesture.bind(self)
|
||||||
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)
|
||||||
|
|
||||||
def on_resize(self):
|
def on_resize(self):
|
||||||
self.view.on_resize()
|
self.view.on_resize()
|
||||||
@ -154,6 +155,9 @@ class ReadUI:
|
|||||||
def update_font_size(self):
|
def update_font_size(self):
|
||||||
self.view.update_font_size()
|
self.view.update_font_size()
|
||||||
|
|
||||||
|
def goto_bookpos(self, bookpos):
|
||||||
|
return self.view.goto_bookpos(bookpos)
|
||||||
|
|
||||||
def update_color_scheme(self):
|
def update_color_scheme(self):
|
||||||
self.view.update_color_scheme()
|
self.view.update_color_scheme()
|
||||||
|
|
||||||
|
@ -30,6 +30,7 @@ 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
|
||||||
from utils import html_escape, is_ios, parse_url_params, username_key
|
from utils import html_escape, is_ios, parse_url_params, username_key
|
||||||
|
from widgets import create_button
|
||||||
|
|
||||||
add_extra_css(def():
|
add_extra_css(def():
|
||||||
sel = '.book-side-margin'
|
sel = '.book-side-margin'
|
||||||
@ -73,14 +74,14 @@ def show_controls_help():
|
|||||||
))
|
))
|
||||||
|
|
||||||
|
|
||||||
def show_metadata_overlay(mi):
|
def setup_metadata_overlay(title):
|
||||||
container = document.getElementById('book-metadata-overlay')
|
container = document.getElementById('book-metadata-overlay')
|
||||||
clear(container)
|
clear(container)
|
||||||
container.style.display = 'block'
|
container.style.display = 'block'
|
||||||
container.style.backgroundColor = get_color('window-background')
|
container.style.backgroundColor = get_color('window-background')
|
||||||
container.appendChild(E.div(
|
container.appendChild(E.div(
|
||||||
style='padding: 1ex 1em; border-bottom: solid 1px currentColor; display:flex; justify-content: space-between;',
|
style='padding: 1ex 1em; border-bottom: solid 1px currentColor; display:flex; justify-content: space-between;',
|
||||||
E.h2(mi.title),
|
E.h2(title),
|
||||||
E.div(svgicon('close'), style='cursor:pointer', class_='simple-link', onclick=def(event):
|
E.div(svgicon('close'), style='cursor:pointer', class_='simple-link', onclick=def(event):
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
@ -88,11 +89,58 @@ def show_metadata_overlay(mi):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
return container
|
||||||
|
|
||||||
|
|
||||||
|
def show_metadata_overlay(mi):
|
||||||
|
container = setup_metadata_overlay(mi.title)
|
||||||
container.appendChild(E.div(class_=BD_CLASS_NAME, style='padding: 1ex 1em'))
|
container.appendChild(E.div(class_=BD_CLASS_NAME, style='padding: 1ex 1em'))
|
||||||
table = E.table(class_='metadata')
|
table = E.table(class_='metadata')
|
||||||
container.lastChild.appendChild(table)
|
container.lastChild.appendChild(table)
|
||||||
render_metadata(mi, table)
|
render_metadata(mi, table)
|
||||||
|
|
||||||
|
|
||||||
|
def show_location_overlay(current_cfi):
|
||||||
|
container = setup_metadata_overlay(_('Go to location…'))
|
||||||
|
container_id = container.id
|
||||||
|
if current_cfi:
|
||||||
|
container.appendChild(E.div(
|
||||||
|
style='margin: 1rem; 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'
|
||||||
|
),
|
||||||
|
create_button(_('Copy'), action=def():
|
||||||
|
src = document.querySelector(f'#{container_id} [name=current_location]')
|
||||||
|
orig = src.value
|
||||||
|
src.value = current_cfi
|
||||||
|
src.focus()
|
||||||
|
src.select()
|
||||||
|
try:
|
||||||
|
document.execCommand('copy')
|
||||||
|
finally:
|
||||||
|
src.value = orig
|
||||||
|
src.blur()
|
||||||
|
)
|
||||||
|
))
|
||||||
|
container.appendChild(E.div(
|
||||||
|
style='margin: 1rem;',
|
||||||
|
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):
|
||||||
|
document.getElementById(container_id).style.display = 'none'
|
||||||
|
else:
|
||||||
|
error_dialog(_('No such location'), _(
|
||||||
|
'No location {} found').format(src.value))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
def margin_elem(sd, which, id, onclick):
|
def margin_elem(sd, which, id, onclick):
|
||||||
@ -122,6 +170,8 @@ class View:
|
|||||||
self.loaded_resources = {}
|
self.loaded_resources = {}
|
||||||
self.current_progress_frac = self.current_file_progress_frac = 0
|
self.current_progress_frac = self.current_file_progress_frac = 0
|
||||||
self.current_toc_node = self.current_toc_toplevel_node = None
|
self.current_toc_node = self.current_toc_toplevel_node = None
|
||||||
|
self.report_cfi_callbacks = {}
|
||||||
|
self.show_chrome_counter = 0
|
||||||
self.clock_timer_id = 0
|
self.clock_timer_id = 0
|
||||||
sd = get_session_data()
|
sd = get_session_data()
|
||||||
left_margin = E.div(svgicon('caret-left'), style='width:{}px;'.format(sd.get('margin_left', 20)), class_='book-side-margin', id='book-left-margin', onclick=self.left_margin_clicked)
|
left_margin = E.div(svgicon('caret-left'), style='width:{}px;'.format(sd.get('margin_left', 20)), class_='book-side-margin', id='book-left-margin', onclick=self.left_margin_clicked)
|
||||||
@ -269,7 +319,12 @@ class View:
|
|||||||
self.content_popup_overlay.hide()
|
self.content_popup_overlay.hide()
|
||||||
|
|
||||||
def show_chrome(self):
|
def show_chrome(self):
|
||||||
|
self.show_chrome_counter += 1
|
||||||
|
self.get_current_cfi('show-chrome-' + self.show_chrome_counter, self.do_show_chrome)
|
||||||
|
|
||||||
|
def do_show_chrome(self, request_id, cfi_data):
|
||||||
self.hide_overlays()
|
self.hide_overlays()
|
||||||
|
self.on_update_cfi(cfi_data)
|
||||||
self.overlay.show()
|
self.overlay.show()
|
||||||
|
|
||||||
def show_search(self):
|
def show_search(self):
|
||||||
@ -451,6 +506,9 @@ class View:
|
|||||||
mi = self.book.metadata
|
mi = self.book.metadata
|
||||||
show_metadata_overlay(mi)
|
show_metadata_overlay(mi)
|
||||||
|
|
||||||
|
def ask_for_location(self):
|
||||||
|
show_location_overlay(self.currently_showing.bookpos)
|
||||||
|
|
||||||
def on_scroll_to_anchor(self, data):
|
def on_scroll_to_anchor(self, data):
|
||||||
self.show_name(data.name, initial_position={'type':'anchor', 'anchor':data.frag, 'replace_history':False})
|
self.show_name(data.name, initial_position={'type':'anchor', 'anchor':data.frag, 'replace_history':False})
|
||||||
|
|
||||||
@ -463,6 +521,8 @@ class View:
|
|||||||
name = cfiname
|
name = cfiname
|
||||||
pos.type, pos.cfi = 'cfi', internal_cfi
|
pos.type, pos.cfi = 'cfi', internal_cfi
|
||||||
self.show_name(name, initial_position=pos)
|
self.show_name(name, initial_position=pos)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def goto_named_destination(self, name, frag):
|
def goto_named_destination(self, name, frag):
|
||||||
if self.currently_showing.name is name:
|
if self.currently_showing.name is name:
|
||||||
@ -507,12 +567,15 @@ class View:
|
|||||||
if toc_node:
|
if toc_node:
|
||||||
self.goto_named_destination(toc_node.dest, toc_node.frag)
|
self.goto_named_destination(toc_node.dest, toc_node.frag)
|
||||||
|
|
||||||
def get_current_cfi(self, request_id):
|
def get_current_cfi(self, request_id, callback):
|
||||||
|
self.report_cfi_callbacks[request_id] = callback
|
||||||
self.iframe_wrapper.send_message('get_current_cfi', request_id=request_id)
|
self.iframe_wrapper.send_message('get_current_cfi', request_id=request_id)
|
||||||
|
|
||||||
def on_report_cfi(self, data):
|
def on_report_cfi(self, data):
|
||||||
ui_operations.report_cfi(
|
cb = self.report_cfi_callbacks[data.request_id]
|
||||||
data.request_id, {'cfi': data.cfi, 'progress_frac': data.progress_frac, 'file_progress_frac': data.file_progress_frac})
|
if cb:
|
||||||
|
cb(data.request_id, {'cfi': data.cfi, 'progress_frac': data.progress_frac, 'file_progress_frac': data.file_progress_frac})
|
||||||
|
v'delete self.report_cfi_callbacks[data.request_id]'
|
||||||
|
|
||||||
def on_update_cfi(self, data):
|
def on_update_cfi(self, data):
|
||||||
overlay_shown = not self.processing_spine_item_display and self.overlay.is_visible
|
overlay_shown = not self.processing_spine_item_display and self.overlay.is_visible
|
||||||
|
@ -216,7 +216,7 @@ def full_screen_state_changed(viewer_in_full_screen):
|
|||||||
|
|
||||||
@from_python
|
@from_python
|
||||||
def get_current_cfi(request_id):
|
def get_current_cfi(request_id):
|
||||||
view.get_current_cfi(request_id)
|
view.get_current_cfi(request_id, ui_operations.report_cfi)
|
||||||
|
|
||||||
|
|
||||||
def onerror(msg, script_url, line_number, column_number, error_object):
|
def onerror(msg, script_url, line_number, column_number, error_object):
|
||||||
@ -251,6 +251,8 @@ if window is window.top:
|
|||||||
view.update_color_scheme()
|
view.update_color_scheme()
|
||||||
ui_operations.update_font_size = def():
|
ui_operations.update_font_size = def():
|
||||||
view.update_font_size()
|
view.update_font_size()
|
||||||
|
ui_operations.goto_bookpos = def(cfi):
|
||||||
|
return view.goto_bookpos(cfi)
|
||||||
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