From e4a19c8808d8ab06c8963f0bafb9687f5b0b0566 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 29 Apr 2020 14:53:35 +0530 Subject: [PATCH] Work on highlights panel --- src/calibre/gui2/viewer/highlights.py | 44 +++++++++++++++++++++++++++ src/calibre/gui2/viewer/search.py | 23 +++++++------- src/calibre/gui2/viewer/ui.py | 18 ++++++++++- src/calibre/gui2/viewer/web_view.py | 3 ++ src/pyj/read_book/shortcuts.pyj | 9 ++++-- src/pyj/read_book/view.pyj | 2 ++ src/pyj/viewer-main.pyj | 2 ++ 7 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 src/calibre/gui2/viewer/highlights.py diff --git a/src/calibre/gui2/viewer/highlights.py b/src/calibre/gui2/viewer/highlights.py new file mode 100644 index 0000000000..394f5024e2 --- /dev/null +++ b/src/calibre/gui2/viewer/highlights.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python2 +# vim:fileencoding=utf-8 +# License: GPL v3 Copyright: 2020, Kovid Goyal + +from __future__ import absolute_import, division, print_function, unicode_literals + +from PyQt5.Qt import QListWidget, QListWidgetItem, Qt, QVBoxLayout, QWidget + +from calibre.gui2.viewer.search import SearchInput + + +class Highlights(QListWidget): + + def __init__(self, parent=None): + QListWidget.__init__(self, parent) + self.setFocusPolicy(Qt.NoFocus) + self.setSpacing(2) + + def load(self, highlights): + self.clear() + for h in highlights: + i = QListWidgetItem(h['highlighted_text'], self) + i.setData(Qt.UserRole, h) + + +class HighlightsPanel(QWidget): + + def __init__(self, parent=None): + QWidget.__init__(self, parent) + self.l = l = QVBoxLayout(self) + l.setContentsMargins(0, 0, 0, 0) + self.search_input = si = SearchInput(self, 'highlights-search') + si.do_search.connect(self.search_requested) + l.addWidget(si) + + self.highlights = h = Highlights(self) + l.addWidget(h) + self.load = h.load + + def search_requested(self, query): + pass + + def focus(self): + self.highlights_list.setFocus(Qt.OtherFocusReason) diff --git a/src/calibre/gui2/viewer/search.py b/src/calibre/gui2/viewer/search.py index 5efc6c28ed..e82ef6d015 100644 --- a/src/calibre/gui2/viewer/search.py +++ b/src/calibre/gui2/viewer/search.py @@ -225,7 +225,7 @@ class SearchInput(QWidget): # {{{ do_search = pyqtSignal(object) - def __init__(self, parent=None): + def __init__(self, parent=None, panel_name='search'): QWidget.__init__(self, parent) self.ignore_search_type_changes = False self.l = l = QVBoxLayout(self) @@ -235,7 +235,8 @@ class SearchInput(QWidget): # {{{ l.addLayout(h) self.search_box = sb = SearchBox(self) - sb.initialize('viewer-search-panel-expression') + self.panel_name = panel_name + sb.initialize('viewer-{}-panel-expression'.format(panel_name)) sb.item_selected.connect(self.saved_search_selected) sb.history_saved.connect(self.history_saved) sb.lineEdit().setPlaceholderText(_('Search')) @@ -271,35 +272,35 @@ class SearchInput(QWidget): # {{{ '
  • Whole words will search for whole words that equal the entered text.' '
  • Regex will interpret the text as a regular expression.' ))) - qt.setCurrentIndex(qt.findData(vprefs.get('viewer-search-mode', 'normal') or 'normal')) + qt.setCurrentIndex(qt.findData(vprefs.get('viewer-{}-mode'.format(self.panel_name), 'normal') or 'normal')) qt.currentIndexChanged.connect(self.save_search_type) h.addWidget(qt) self.case_sensitive = cs = QCheckBox(_('&Case sensitive'), self) cs.setFocusPolicy(Qt.NoFocus) - cs.setChecked(bool(vprefs.get('viewer-search-case-sensitive', False))) + cs.setChecked(bool(vprefs.get('viewer-{}-case-sensitive'.format(self.panel_name), False))) cs.stateChanged.connect(self.save_search_type) h.addWidget(cs) def history_saved(self, new_text, history): if new_text: - sss = vprefs.get('saved-search-settings') or {} + sss = vprefs.get('saved-{}-settings'.format(self.panel_name)) or {} sss[new_text] = {'case_sensitive': self.case_sensitive.isChecked(), 'mode': self.query_type.currentData()} history = frozenset(history) sss = {k: v for k, v in iteritems(sss) if k in history} - vprefs['saved-search-settings'] = sss + vprefs['saved-{}-settings'.format(self.panel_name)] = sss def save_search_type(self): text = self.search_box.currentText() if text and not self.ignore_search_type_changes: - sss = vprefs.get('saved-search-settings') or {} + sss = vprefs.get('saved-{}-settings'.format(self.panel_name)) or {} sss[text] = {'case_sensitive': self.case_sensitive.isChecked(), 'mode': self.query_type.currentData()} - vprefs['saved-search-settings'] = sss + vprefs['saved-{}-settings'.format(self.panel_name)] = sss def saved_search_selected(self): text = self.search_box.currentText() if text: - s = (vprefs.get('saved-search-settings') or {}).get(text) + s = (vprefs.get('saved-{}-settings'.format(self.panel_name)) or {}).get(text) if s: self.ignore_search_type_changes = True if 'case_sensitive' in s: @@ -320,8 +321,8 @@ class SearchInput(QWidget): # {{{ ) def emit_search(self, backwards=False): - vprefs['viewer-search-case-sensitive'] = self.case_sensitive.isChecked() - vprefs['viewer-search-mode'] = self.query_type.currentData() + vprefs['viewer-{}-case-sensitive'.format(self.panel_name)] = self.case_sensitive.isChecked() + vprefs['viewer-{}-mode'.format(self.panel_name)] = self.query_type.currentData() sq = self.search_query(backwards) if sq is not None: self.do_search.emit(sq) diff --git a/src/calibre/gui2/viewer/ui.py b/src/calibre/gui2/viewer/ui.py index ba7bf21b47..7840658adf 100644 --- a/src/calibre/gui2/viewer/ui.py +++ b/src/calibre/gui2/viewer/ui.py @@ -32,6 +32,7 @@ from calibre.gui2.viewer.bookmarks import BookmarkManager from calibre.gui2.viewer.convert_book import ( clean_running_workers, prepare_book, update_book ) +from calibre.gui2.viewer.highlights import HighlightsPanel from calibre.gui2.viewer.lookup import Lookup from calibre.gui2.viewer.overlay import LoadingOverlay from calibre.gui2.viewer.search import SearchPanel @@ -73,6 +74,7 @@ def dock_defs(): d(_('Bookmarks'), 'bookmarks', Qt.RightDockWidgetArea) d(_('Search'), 'search', Qt.LeftDockWidgetArea) d(_('Inspector'), 'inspector', Qt.RightDockWidgetArea, Qt.AllDockWidgetAreas) + d(_('Highlights'), 'highlights', Qt.RightDockWidgetArea) return ans @@ -152,6 +154,9 @@ class EbookViewer(MainWindow): w.toggle_requested.connect(self.toggle_bookmarks) self.bookmarks_dock.setWidget(w) + self.highlights_widget = w = HighlightsPanel(self) + self.highlights_dock.setWidget(w) + self.web_view = WebView(self) self.web_view.cfi_changed.connect(self.cfi_changed) self.web_view.reload_book.connect(self.reload_book) @@ -161,6 +166,7 @@ class EbookViewer(MainWindow): self.search_widget.show_search_result.connect(self.web_view.show_search_result) self.web_view.search_result_not_found.connect(self.search_widget.search_result_not_found) self.web_view.toggle_bookmarks.connect(self.toggle_bookmarks) + self.web_view.toggle_highlights.connect(self.toggle_highlights) self.web_view.new_bookmark.connect(self.bookmarks_widget.create_requested) self.web_view.toggle_inspector.connect(self.toggle_inspector) self.web_view.toggle_lookup.connect(self.toggle_lookup) @@ -301,6 +307,14 @@ class EbookViewer(MainWindow): else: self.bookmarks_widget.bookmarks_list.setFocus(Qt.OtherFocusReason) + def toggle_highlights(self): + is_visible = self.highlights_dock.isVisible() + self.highlights_dock.setVisible(not is_visible) + if is_visible: + self.web_view.setFocus(Qt.OtherFocusReason) + else: + self.highlights_widget.focus() + def toggle_lookup(self): self.lookup_dock.setVisible(not self.lookup_dock.isVisible()) @@ -490,9 +504,11 @@ class EbookViewer(MainWindow): initial_position = {'type': 'ref', 'data': open_at[len('ref:'):]} elif is_float(open_at): initial_position = {'type': 'bookpos', 'data': float(open_at)} + highlights = self.current_book_data['annotations_map']['highlight'] + self.highlights_widget.load(highlights) self.web_view.start_book_load( initial_position=initial_position, - highlights=list(map(serialize_annotation, self.current_book_data['annotations_map']['highlight'])) + highlights=list(map(serialize_annotation, highlights)) ) def load_book_data(self): diff --git a/src/calibre/gui2/viewer/web_view.py b/src/calibre/gui2/viewer/web_view.py index 0b128101a0..08ed380b26 100644 --- a/src/calibre/gui2/viewer/web_view.py +++ b/src/calibre/gui2/viewer/web_view.py @@ -248,6 +248,7 @@ class ViewerBridge(Bridge): reload_book = from_js() toggle_toc = from_js() toggle_bookmarks = from_js() + toggle_highlights = from_js() new_bookmark = from_js() toggle_inspector = from_js() toggle_lookup = from_js() @@ -439,6 +440,7 @@ class WebView(RestartingWebEngineView): search_result_not_found = pyqtSignal(object) find_next = pyqtSignal(object) toggle_bookmarks = pyqtSignal() + toggle_highlights = pyqtSignal() new_bookmark = pyqtSignal() toggle_inspector = pyqtSignal() toggle_lookup = pyqtSignal() @@ -491,6 +493,7 @@ class WebView(RestartingWebEngineView): self.bridge.search_result_not_found.connect(self.search_result_not_found) self.bridge.find_next.connect(self.find_next) self.bridge.toggle_bookmarks.connect(self.toggle_bookmarks) + self.bridge.toggle_highlights.connect(self.toggle_highlights) self.bridge.new_bookmark.connect(self.new_bookmark) self.bridge.toggle_inspector.connect(self.toggle_inspector) self.bridge.toggle_lookup.connect(self.toggle_lookup) diff --git a/src/pyj/read_book/shortcuts.pyj b/src/pyj/read_book/shortcuts.pyj index 78b0b67a29..1c3ede0430 100644 --- a/src/pyj/read_book/shortcuts.pyj +++ b/src/pyj/read_book/shortcuts.pyj @@ -333,12 +333,11 @@ def shortcuts_definition(): } if runtime.in_develop_mode: ans.create_annotation = desc( - "a", + "Ctrl+h", 'ui', _('Create a highlight'), ) - return ans @@ -398,6 +397,12 @@ def add_standalone_viewer_shortcuts(): 'ui', _('Toggle the toolbar'), ) + if runtime.in_develop_mode: + sc['toggle_highlights'] = desc( + "Ctrl+Alt+h", + 'ui', + _('Toggle the highlights panel') + ) def create_shortcut_map(custom_shortcuts): diff --git a/src/pyj/read_book/view.pyj b/src/pyj/read_book/view.pyj index 1862a081f1..334987fd33 100644 --- a/src/pyj/read_book/view.pyj +++ b/src/pyj/read_book/view.pyj @@ -427,6 +427,8 @@ class View: ui_operations.toggle_toc() elif data.name is 'toggle_bookmarks': ui_operations.toggle_bookmarks() + elif data.name is 'toggle_highlights': + ui_operations.toggle_highlights() elif data.name is 'new_bookmark': ui_operations.new_bookmark() elif data.name is 'toggle_inspector': diff --git a/src/pyj/viewer-main.pyj b/src/pyj/viewer-main.pyj index fc15c11e89..8dfeb7f9a6 100644 --- a/src/pyj/viewer-main.pyj +++ b/src/pyj/viewer-main.pyj @@ -357,6 +357,8 @@ if window is window.top: to_python.toggle_toc() ui_operations.toggle_bookmarks = def(): to_python.toggle_bookmarks() + ui_operations.toggle_highlights = def(): + to_python.toggle_highlights() ui_operations.new_bookmark = def(): to_python.new_bookmark() ui_operations.toggle_inspector = def():