From 9e9190726816ff4eeedf85f7ea64f73be53f0841 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 17 Aug 2020 15:24:37 +0530 Subject: [PATCH] Start work on highlights panel for browser viewer --- src/pyj/read_book/annotations.pyj | 4 + src/pyj/read_book/highlights.pyj | 116 +++++++++++++++++++++++++++- src/pyj/read_book/overlay.pyj | 21 +++-- src/pyj/read_book/selection_bar.pyj | 20 +---- 4 files changed, 137 insertions(+), 24 deletions(-) diff --git a/src/pyj/read_book/annotations.pyj b/src/pyj/read_book/annotations.pyj index 6b6a442d27..12364ddef2 100644 --- a/src/pyj/read_book/annotations.pyj +++ b/src/pyj/read_book/annotations.pyj @@ -161,6 +161,10 @@ class AnnotationsManager: # {{{ highlights = highlights or v'[]' self.highlights = {h.uuid: h for h in highlights} + def all_highlights(self): + ans = [h for h in Object.values(self.highlights) if not h.removed].as_array() + return ans + def merge_highlights(self, highlights): highlights = highlights or v'[]' updated = False diff --git a/src/pyj/read_book/highlights.pyj b/src/pyj/read_book/highlights.pyj index c2c42d20fc..53996fc51f 100644 --- a/src/pyj/read_book/highlights.pyj +++ b/src/pyj/read_book/highlights.pyj @@ -7,7 +7,8 @@ from gettext import gettext as _ from book_list.globals import get_session_data from book_list.theme import get_color -from dom import svgicon, unique_id +from complete import create_search_bar +from dom import add_extra_css, build_rule, svgicon, unique_id from modals import error_dialog from widgets import create_button @@ -505,3 +506,116 @@ class EditNotesAndColors: # {{{ style = self.container.getElementsByClassName('current-swatch')[0].dataset.style return HighlightStyle(JSON.parse(style)) # }}} + + +# Browse highlights panel {{{ + +def get_container_id(): + if not get_container_id.ans: + get_container_id.ans = unique_id() + return get_container_id.ans + + +def get_container(): + return document.getElementById(get_container_id()) + + +def find_previous(): + pass + + +def find_next(): + pass + +add_extra_css(def(): + sel = '#' + get_container_id() + ans = '' + qsel = sel + ' .highlight' + ans += build_rule(qsel, margin_top='1ex', border_top='solid 1px', padding_top='1ex', cursor='pointer') + ans += build_rule(qsel + ' .notes', display='none', margin_top='1ex', max_height='20ex', overflow='auto') + ans += build_rule(qsel + ' .actions', display='none', margin_top='1ex', justify_content='space-between') + current = qsel + '.current' + ans += build_rule(current + ' .title', font_weight='bold', font_size='larger') + ans += build_rule(current + ' .notes', display='block') + ans += build_rule(current + ' .actions', display='flex') + return ans +) + + +def render_notes(notes, container): + current_block = '' + def add_para(): + nonlocal current_block + container.appendChild(E.p(current_block)) + if container.childNodes.length > 1: + container.lastChild.style.marginTop = '2ex' + current_block = '' + + for line in notes.splitlines(): + if not line or not line.strip(): + if current_block: + add_para() + continue + current_block += line + '\n' + if current_block: + add_para() + return container + + +def highlight_entry_clicked(ev): + set_current_highlight_entry(ev.currentTarget) + + +def set_current_highlight_entry(entry): + c = get_container() + for h in c.querySelectorAll('.highlight'): + h.classList.remove('current') + entry.classList.add('current') + + +def show_in_text(annot_id, view): + view.highlight_action(annot_id, 'goto') + + +def highlight_entry(h, onclick): + + def action(func, ev): + ev.stopPropagation(), ev.preventDefault() + onclick(func) + + ans = E.div( + class_='highlight', + onclick=highlight_entry_clicked, + E.div(class_='title', h.highlighted_text), + E.div( + class_='actions', + E.a(class_='blue-link', _('Show in text'), onclick=action.bind(None, show_in_text.bind(None, h.uuid))), + '\xa0\xa0', + E.a(class_='blue-link', _('Remove highlight')), + ), + E.div(class_='notes') + ) + if h.notes: + render_notes(h.notes, ans.querySelector('.notes')) + return ans + + +def create_highlights_panel(annotations_manager, book, container, onclick): + next_button = E.div(style='margin-left: 1rem', class_='simple-link', svgicon('chevron-down'), title=_('Next match')) + prev_button = E.div(style='margin-left: 1rem', class_='simple-link', svgicon('chevron-up'), title=_('Previous match')) + prev_button.addEventListener('click', def(ev): find_previous();) + sb = create_search_bar(find_next, 'search-in-highlights', placeholder=_('Search') + '…', button=next_button, associated_widgets=[prev_button]) + sb.style.flexGrow = '10' + c = E.div( + style='margin: 1rem', + id=get_container_id(), + E.div( + style='display: flex', + sb, next_button, prev_button + ), + ) + container.appendChild(c) + c.appendChild(E.div()) + for h in annotations_manager.all_highlights(): + c.lastChild.appendChild(highlight_entry(h, onclick)) +# }}} diff --git a/src/pyj/read_book/overlay.pyj b/src/pyj/read_book/overlay.pyj index 444b2c6767..2d52875f75 100644 --- a/src/pyj/read_book/overlay.pyj +++ b/src/pyj/read_book/overlay.pyj @@ -21,6 +21,7 @@ from modals import error_dialog from read_book.bookmarks import create_bookmarks_panel from read_book.globals import runtime, ui_operations from read_book.goto import create_goto_panel, create_location_overlay +from read_book.highlights import create_highlights_panel from read_book.open_book import create_open_book from read_book.prefs.font_size import create_font_size_panel from read_book.prefs.main import create_prefs_panel @@ -271,9 +272,13 @@ class MainOverlay: # {{{ reload_actions = E.ul(sync_action, delete_action, reload_action) bookmarks_action = ac(_('Bookmarks'), None, self.overlay.show_bookmarks, 'bookmark') + highlights_action = ac( + _('Browse highlights'), _('Browse all highlights'), def(): + self.overlay.show_highlights() + , 'image') + toc_actions = E.ul(ac(_('Table of Contents'), None, self.overlay.show_toc, 'toc')) - toc_actions.appendChild(bookmarks_action) toc_actions.appendChild(ac(_('Reference mode'), _('Toggle the Reference mode'), self.overlay.toggle_reference_mode, 'reference-mode')) actions_div = E.div( # actions @@ -288,6 +293,8 @@ class MainOverlay: # {{{ toc_actions, + E.ul(highlights_action, bookmarks_action), + E.ul( ac(_('Font size'), _('Change text size'), self.overlay.show_font_size_chooser, 'Aa', True), ac(_('Preferences'), _('Configure the book viewer'), self.overlay.show_prefs, 'cogs'), @@ -331,10 +338,6 @@ class MainOverlay: # {{{ ac(_('Lookup/search word'), _('Lookup or search for the currently selected word'), def(): self.overlay.hide(), ui_operations.toggle_lookup(True);, 'library') )) - actions_div.lastChild.appendChild( - ac(_('Browse highlights'), _('Browse all highlights'), - def(): self.overlay.hide(), ui_operations.toggle_highlights();, 'image') - ) copy_actions = E.ul() if self.elements.link: copy_actions.appendChild(ac(_('Copy link'), _('Copy the current link'), def(): @@ -671,6 +674,14 @@ class Overlay: self.show_current_panel() self.view.get_current_cfi('show-bookmarks', do_it) + def show_highlights(self): + if ui_operations.toggle_highlights: + self.hide() + ui_operations.toggle_highlights() + return + self.panels.push(TOCOverlay(self, create_highlights_panel.bind(None, self.view.annotations_manager), _('Highlights'))) + self.show_current_panel() + def show_goto(self): self.hide_current_panel() self.panels.push(TOCOverlay(self, create_goto_panel.bind(None, self.view.current_position_data), _('Go to…'))) diff --git a/src/pyj/read_book/selection_bar.pyj b/src/pyj/read_book/selection_bar.pyj index a963e01e37..af6c49fdbf 100644 --- a/src/pyj/read_book/selection_bar.pyj +++ b/src/pyj/read_book/selection_bar.pyj @@ -12,7 +12,7 @@ from dom import clear, svgicon, unique_id from modals import error_dialog, question_dialog from read_book.globals import runtime, ui_operations from read_book.highlights import ( - ICON_SIZE, EditNotesAndColors, HighlightStyle, all_styles + ICON_SIZE, EditNotesAndColors, HighlightStyle, all_styles, render_notes ) from read_book.shortcuts import shortcut_for_key_event @@ -340,23 +340,7 @@ class SelectionBar: c.style.maxHeight = 'min(20ex, 40vh)' else: c.style.maxHeight = '20ex' - current_block = '' - - def add_para(): - nonlocal current_block - c.appendChild(E.p(current_block)) - if c.childNodes.length > 1: - c.lastChild.style.marginTop = '2ex' - current_block = '' - - for line in notes.splitlines(): - if not line or not line.strip(): - if current_block: - add_para() - continue - current_block += line + '\n' - if current_block: - add_para() + render_notes(notes, c) # }}} # accessors {{{