diff --git a/src/pyj/read_book/iframe.pyj b/src/pyj/read_book/iframe.pyj index 649a5a4ebb..cd47ecabe1 100644 --- a/src/pyj/read_book/iframe.pyj +++ b/src/pyj/read_book/iframe.pyj @@ -29,6 +29,7 @@ from read_book.paged_mode import ( scroll_by_page as paged_scroll_by_page, scroll_to_elem, scroll_to_fraction as paged_scroll_to_fraction, snap_to_selection ) +from read_book.referencing import end_reference_mode, start_reference_mode from read_book.resources import finalize_resources, unserialize_html from read_book.settings import ( apply_colors, apply_font_size, apply_settings, apply_stylesheet, opts, @@ -80,6 +81,7 @@ class IframeBoss: def __init__(self): window.navigator.epubReadingSystem = EPUBReadingSystem() self.last_cfi = None + self.reference_mode_enabled = False self.replace_history_on_next_cfi_update = True self.blob_url_map = {} self.content_ready = False @@ -100,6 +102,7 @@ class IframeBoss: 'window_size': self.received_window_size, 'get_current_cfi': self.get_current_cfi, 'set_forward_keypresses': self.set_forward_keypresses, + 'set_reference_mode': self.set_reference_mode, } self.comm = IframeClient(handlers) self.last_window_ypos = 0 @@ -155,6 +158,7 @@ class IframeBoss: self.content_ready = False self.replace_history_on_next_cfi_update = True self.book = current_book.book = data.book + self.reference_mode_enabled = data.reference_mode_enabled self.is_titlepage = data.is_titlepage spine = self.book.manifest.spine index = spine.indexOf(data.name) @@ -178,7 +182,13 @@ class IframeBoss: self.anchor_funcs = paged_anchor_funcs update_settings(data.settings) self.keyboard_shortcut_map = create_shortcut_map(data.settings.keyboard_shortcuts) - set_current_spine_item({'name':data.name, 'is_first':index is 0, 'is_last':index is spine.length - 1, 'initial_position':data.initial_position}) + set_current_spine_item({ + 'name':data.name, + 'is_first':index is 0, + 'is_last':index is spine.length - 1, + 'index': index, + 'initial_position':data.initial_position + }) self.last_cfi = None for name in self.blob_url_map: window.URL.revokeObjectURL(self.blob_url_map[name]) @@ -239,6 +249,8 @@ class IframeBoss: if self.is_titlepage and not opts.cover_preserve_aspect_ratio: document.body.classList.add('cover-fill') document.body.classList.add(f'calibre-viewer-{layout_style()}') + if self.reference_mode_enabled: + start_reference_mode() self.last_window_width, self.last_window_height = scroll_viewport.width(), scroll_viewport.height() apply_settings() fix_fullscreen_svg_images() @@ -269,8 +281,7 @@ class IframeBoss: self.find(ipos.search_data, True) spine = self.book.manifest.spine files = self.book.manifest.files - current_name = current_spine_item().name - spine_index = spine.indexOf(current_name) + spine_index = csi.index self.length_before = 0 if spine_index > -1: for i in range(spine_index): @@ -297,9 +308,7 @@ class IframeBoss: cfi = current_cfi() selected_text = window.getSelection().toString() if cfi: - spine = self.book.manifest.spine - current_name = current_spine_item().name - index = spine.indexOf(current_name) + index = current_spine_item().index if index > -1: cfi = 'epubcfi(/{}{})'.format(2*(index+1), cfi) self.send_message( @@ -312,9 +321,7 @@ class IframeBoss: def update_cfi(self): cfi = current_cfi() if cfi: - spine = self.book.manifest.spine - current_name = current_spine_item().name - index = spine.indexOf(current_name) + index = current_spine_item().index if index > -1: cfi = 'epubcfi(/{}{})'.format(2*(index+1), cfi) pf = self.calculate_progress_frac() @@ -472,6 +479,16 @@ class IframeBoss: else: self.send_message('find_in_spine', text=data.text, backwards=data.backwards, searched_in_spine=data.searched_in_spine) + def reference_item_changed(self, ref_num_or_none): + self.send_message('reference_item_changed', refnum=ref_num_or_none, index=current_spine_item().index) + + def set_reference_mode(self, data): + self.reference_mode_enabled = data.enabled + if data.enabled: + start_reference_mode() + else: + end_reference_mode() + def main(): main.boss = IframeBoss() diff --git a/src/pyj/read_book/referencing.pyj b/src/pyj/read_book/referencing.pyj new file mode 100644 index 0000000000..ad437ada20 --- /dev/null +++ b/src/pyj/read_book/referencing.pyj @@ -0,0 +1,35 @@ +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2019, Kovid Goyal +# noqa: eol-semicolon +from __python__ import bound_methods, hash_literals + +from read_book.globals import get_boss + + +def on_mouse_over(ev): + p = this + if p.dataset.calibreRefNum: + refnum = int(p.dataset.calibreRefNum) + get_boss().reference_item_changed(refnum) + + +def on_mouse_out(ev): + get_boss().reference_item_changed(None) + + +def start_reference_mode(): + i = 0 + for p in document.getElementsByTagName('p'): + i += 1 + if not p.dataset.calibreRefNum: + p.dataset.calibreRefNum = i + '' + p.addEventListener('mouseover', on_mouse_over, {'passive': True}) + p.addEventListener('mouseout', on_mouse_out, {'passive': True}) + + +def end_reference_mode(): + for p in document.getElementsByTagName('p'): + if p.dataset.calibreRefNum: + p.removeEventListener('mouseover', on_mouse_over, {'passive': True}) + p.removeEventListener('mouseout', on_mouse_out, {'passive': True}) + v'delete p.dataset.calibreRefNum' diff --git a/src/pyj/read_book/shortcuts.pyj b/src/pyj/read_book/shortcuts.pyj index 11569e8100..efd4c3e112 100644 --- a/src/pyj/read_book/shortcuts.pyj +++ b/src/pyj/read_book/shortcuts.pyj @@ -228,6 +228,12 @@ def shortcuts_definition(): _('Toggle between Paged mode and Flow mode for text layout') ), + 'toggle_reference_mode': desc( + 'Ctrl+x', + 'ui', + _('Toggle the Reference mode') + ), + 'reload_book': desc( v"['F5', 'Ctrl+r']", 'ui', diff --git a/src/pyj/read_book/view.pyj b/src/pyj/read_book/view.pyj index bba43543c7..68d6b79efa 100644 --- a/src/pyj/read_book/view.pyj +++ b/src/pyj/read_book/view.pyj @@ -147,6 +147,7 @@ class View: def __init__(self, container): self.timers = Timers() + self.reference_mode_enabled = False self.loaded_resources = {} self.current_progress_frac = self.current_file_progress_frac = 0 self.current_toc_node = self.current_toc_toplevel_node = None @@ -190,8 +191,13 @@ class View: E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; overflow: auto; display:none', id='book-overlay'), # main overlay E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none', id='controls-help-overlay'), # controls help overlay ) - ) - ) + ), + E.div( + style=f'color: {get_color("window-foreground")}; background: {get_color("window-background")};' + + 'position: absolute; display: none; left: 0; top: 0; padding: 0.5ex; border: solid 2px; z-index: 3000', + id='reference-mode-overlay' + ), + ), ) handlers = { 'ready': self.on_iframe_ready, @@ -216,6 +222,7 @@ class View: 'selectionchange': self.on_selection_change, 'handle_shortcut': self.on_handle_shortcut, 'handle_keypress': self.on_handle_keypress, + 'reference_item_changed': self.on_reference_item_changed, } entry_point = None if runtime.is_standalone_viewer else 'read_book.iframe' if runtime.is_standalone_viewer: @@ -236,6 +243,10 @@ class View: def iframe(self): return self.iframe_wrapper.iframe + @property + def reference_mode_overlay(self): + return document.getElementById('reference-mode-overlay') + def on_lookup_word(self, data): if runtime.is_standalone_viewer: ui_operations.selection_changed(data.word) @@ -366,6 +377,8 @@ class View: self.bump_font_size({'increase': False}) elif data.name is 'toggle_full_screen': ui_operations.toggle_full_screen() + elif data.name is 'toggle_reference_mode': + self.toggle_reference_mode() elif data.name is 'reload_book': ui_operations.reload_book() elif data.name is 'search_for_selection': @@ -456,6 +469,7 @@ class View: self.overlay.hide() self.search_overlay.hide() self.content_popup_overlay.hide() + self.reference_mode_overlay.style.display = 'none' self.focus_iframe() def focus_iframe(self): @@ -932,7 +946,7 @@ class View: self.iframe_wrapper.send_unencrypted_message('display', resource_data=resource_data, book=self.book, name=self.currently_showing.name, initial_position=self.currently_showing.initial_position, - settings=self.currently_showing.settings, + settings=self.currently_showing.settings, reference_mode_enabled=self.reference_mode_enabled, is_titlepage=self.currently_showing.name is self.book.manifest.title_page_name, ) @@ -941,6 +955,7 @@ class View: self.hide_loading() self.set_progress_frac(data.progress_frac, data.file_progress_frac) self.update_header_footer() + self.on_reference_item_changed() window.scrollTo(0, 0) # ensure window is at 0 on mobile where the navbar causes issues if self.book_load_started: self.book_load_started = False @@ -958,3 +973,17 @@ class View: def update_color_scheme(self): cs = self.get_color_scheme(True) self.iframe_wrapper.send_message('change_color_scheme', color_scheme=cs) + + def toggle_reference_mode(self): + self.reference_mode_enabled = not self.reference_mode_enabled + self.iframe_wrapper.send_message('set_reference_mode', enabled=self.reference_mode_enabled) + + def on_reference_item_changed(self, data): + data = data or {'refnum': None, 'index': None} + refnum, index = data.refnum, data.index + div = self.reference_mode_overlay + if refnum is None: + div.style.display = 'none' + else: + div.style.display = 'block' + div.textContent = f'{index}.{refnum}'