From 943c830859a7d3452370973e8c891736bcf313d7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 8 Feb 2021 23:02:51 +0530 Subject: [PATCH] E-book viewer: Show the URL when hovering over external links. Fixes #1911107 [Enhancement Request: ebook-viewer: Preview link destination](https://bugs.launchpad.net/calibre/+bug/1911107) --- src/calibre/gui2/viewer/web_view.py | 6 ++++++ src/pyj/read_book/prefs/head_foot.pyj | 6 ++++-- src/pyj/read_book/view.pyj | 23 +++++++++++++++++------ src/pyj/viewer-main.pyj | 2 ++ 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/calibre/gui2/viewer/web_view.py b/src/calibre/gui2/viewer/web_view.py index 9b54b7679c..9a8d86ad2e 100644 --- a/src/calibre/gui2/viewer/web_view.py +++ b/src/calibre/gui2/viewer/web_view.py @@ -496,6 +496,7 @@ class WebView(RestartingWebEngineView): self.show_home_page_on_ready = True self._size_hint = QSize(int(w/3), int(w/2)) self._page = WebPage(self) + self._page.linkHovered.connect(self.link_hovered) self.view_is_ready = False self.bridge.bridge_ready.connect(self.on_bridge_ready) self.bridge.view_created.connect(self.on_view_created) @@ -549,6 +550,11 @@ class WebView(RestartingWebEngineView): self.inspector = Inspector(parent.inspector_dock.toggleViewAction(), self) parent.inspector_dock.setWidget(self.inspector) + def link_hovered(self, url): + if url == 'javascript:void(0)': + url = '' + self.generic_action('show-status-message', {'text': url}) + def shutdown(self): self.tts.shutdown() diff --git a/src/pyj/read_book/prefs/head_foot.pyj b/src/pyj/read_book/prefs/head_foot.pyj index d40e52783b..4d2ff9739b 100644 --- a/src/pyj/read_book/prefs/head_foot.pyj +++ b/src/pyj/read_book/prefs/head_foot.pyj @@ -165,13 +165,15 @@ def format_pos(progress_frac, length): return f'{pos:.1f} / {pages}' -def render_head_foot(div, which, region, 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, override): 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': + if override: + text = override + elif field is 'progress': percent = min(100, max(Math.round(pos.progress_frac * 100), 0)) text = percent + '%' elif field is 'title': diff --git a/src/pyj/read_book/view.pyj b/src/pyj/read_book/view.pyj index 0d2032ab44..9771c9f88c 100644 --- a/src/pyj/read_book/view.pyj +++ b/src/pyj/read_book/view.pyj @@ -209,6 +209,7 @@ class View: self.loaded_resources = {} self.current_progress_frac = self.current_file_progress_frac = 0 self.current_page_counts = {'current': 0, 'total': 0, 'pages_per_screen': 1} + self.current_status_message = '' self.current_toc_node = self.current_toc_toplevel_node = None self.current_toc_families = v'[]' self.report_cfi_callbacks = {} @@ -1244,26 +1245,35 @@ class View: } return pos + def show_status_message(self, msg): + self.current_status_message = msg or '' + self.update_header_footer() + if self.current_status_message: + window.setTimeout(def(): self.show_status_message();, 10000) + def update_header_footer(self): sd = get_session_data() has_clock = False pos = self.current_position_data + if not self.book: + return 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) - def render_template(div, sz_attr, name): + def render_template(div, edge, name): nonlocal has_clock c = div.lastChild b = c.previousSibling a = b.previousSibling - if sd.get(sz_attr, 20) > 5: + if sd.get(f'margin_{edge}', 20) > 5: mi = self.book.metadata - hca = render_head_foot(a, name, 'left', mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time, pos) - hcb = render_head_foot(b, name, 'middle', mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time, pos) - hcc = render_head_foot(c, name, 'right', mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time, pos) + override = self.current_status_message if edge is 'bottom' else '' + hca = render_head_foot(a, name, 'left', mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time, pos, override) + hcb = render_head_foot(b, name, 'middle', mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time, pos, '') + hcc = render_head_foot(c, name, 'right', mi, self.current_toc_node, self.current_toc_toplevel_node, book_time, chapter_time, pos, '') if hca or hcb or hcc: has_clock = True else: @@ -1273,7 +1283,7 @@ class View: div = document.getElementById(f'book-{edge}-margin') if div: tname = {'left':'left-margin', 'right': 'right-margin', 'top': 'header', 'bottom': 'footer'}[edge] - render_template(div, f'margin_{edge}', tname) + render_template(div, edge, tname) if has_clock: if not self.timer_ids.clock: self.timer_ids.clock = window.setInterval(self.update_header_footer, 60000) @@ -1310,6 +1320,7 @@ class View: # We cannot encrypt this message because the resource data contains # Blob objects which do not survive encryption self.processing_spine_item_display = True + self.current_status_message = '' self.iframe.style.visibility = 'hidden' self.iframe_wrapper.send_unencrypted_message('display', resource_data=resource_data, book=self.book, name=self.currently_showing.name, diff --git a/src/pyj/viewer-main.pyj b/src/pyj/viewer-main.pyj index 2d74cea0e1..56afbf580b 100644 --- a/src/pyj/viewer-main.pyj +++ b/src/pyj/viewer-main.pyj @@ -207,6 +207,8 @@ def highlight_action(uuid, which): def generic_action(which, data): if which is 'set-notes-in-highlight': view.set_notes_for_highlight(data.uuid, data.notes or '') + if which is 'show-status-message': + view.show_status_message(data.text) @from_python