From c363d790abc04e8334957683a94d08cc58b7cd76 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 13 Sep 2020 16:02:41 +0530 Subject: [PATCH] More accurate detection of current ToC section when highlighting --- src/pyj/read_book/iframe.pyj | 6 ++- src/pyj/read_book/selection_bar.pyj | 10 +++- src/pyj/read_book/toc.pyj | 84 +++++++++++++++++++++-------- 3 files changed, 75 insertions(+), 25 deletions(-) diff --git a/src/pyj/read_book/iframe.pyj b/src/pyj/read_book/iframe.pyj index 2fcbd2feea..7de617e1a0 100644 --- a/src/pyj/read_book/iframe.pyj +++ b/src/pyj/read_book/iframe.pyj @@ -57,7 +57,7 @@ from read_book.settings import ( from read_book.shortcuts import ( create_shortcut_map, keyevent_as_shortcut, shortcut_for_key_event ) -from read_book.toc import update_visible_toc_anchors +from read_book.toc import update_visible_toc_anchors, find_anchor_before_range from read_book.touch import ( create_handlers as create_touch_handlers, reset_handlers as reset_touch_handlers ) @@ -793,9 +793,10 @@ class IframeBoss: sel.removeAllRanges() elif dtype is 'apply-highlight': sel = window.getSelection() - text = sel.toString() if not sel.rangeCount: return + anchor_before = find_anchor_before_range(sel.getRangeAt(0), self.book.manifest.toc_anchor_map, self.anchor_funcs) + text = sel.toString() bounds = cfi_for_selection() style = highlight_style_as_css(data.style, opts.is_dark_theme, opts.color_scheme.foreground) cls = 'crw-has-dot' if data.has_notes else None @@ -824,6 +825,7 @@ class IframeBoss: bounds=bounds, removed_highlights=removed_highlights, highlighted_text=text, + anchor_before=anchor_before ) reset_find_caches() else: diff --git a/src/pyj/read_book/selection_bar.pyj b/src/pyj/read_book/selection_bar.pyj index 901ec82eec..62473a82c6 100644 --- a/src/pyj/read_book/selection_bar.pyj +++ b/src/pyj/read_book/selection_bar.pyj @@ -15,6 +15,7 @@ from read_book.highlights import ( ICON_SIZE, EditNotesAndColors, HighlightStyle, all_styles, render_notes ) from read_book.shortcuts import shortcut_for_key_event +from read_book.toc import get_toc_nodes_bordering_spine_item, family_for_toc_node DRAG_SCROLL_ZONE_MIN_HEIGHT = 10 BUTTON_MARGIN = '0.5rem' @@ -965,8 +966,15 @@ class SelectionBar: _('Highlighting failed'), _('Failed to apply highlighting, try adjusting extent of highlight') ) + toc_family = v'[]' + if msg.anchor_before: + toc_family = family_for_toc_node(msg.anchor_before.id) + else: + before = get_toc_nodes_bordering_spine_item()[0] + if before: + toc_family = family_for_toc_node(before.id) self.annotations_manager.add_highlight( - msg, self.current_highlight_style.style, notes, self.view.current_toc_family) + msg, self.current_highlight_style.style, notes, toc_family) elif msg.type is 'edit-highlight': if self.state is WAITING: self.create_highlight() diff --git a/src/pyj/read_book/toc.pyj b/src/pyj/read_book/toc.pyj index b4a9517b0b..35fe63d93f 100644 --- a/src/pyj/read_book/toc.pyj +++ b/src/pyj/read_book/toc.pyj @@ -24,28 +24,20 @@ def iter_toc_descendants(node, callback): iter_toc_descendants(child, callback) -def get_border_nodes(toc, id_map): - data = update_visible_toc_nodes.data - before, after = data.before, data.after - if before: - before = id_map[before] - if after: - after = id_map[after] - if before and after: - # Both border nodes are in the current spine item - return before, after +def get_toc_nodes_bordering_spine_item(toc, csi): # Find the ToC entries that point to the closest files on either side of the - # current spine item + # spine item + toc = toc or current_book().manifest.toc + csi = csi or current_spine_item() spine = current_book().manifest.spine spine_before, spine_after = {}, {} which = spine_before - csi = current_spine_item() + before = after = prev = None for name in spine: if name is csi: which = spine_after else: which[name] = True - prev = None iter_toc_descendants(toc, def(node): nonlocal prev, before, after if node.dest: @@ -63,6 +55,37 @@ def get_border_nodes(toc, id_map): return before, after +def get_border_nodes(toc, id_map): + data = update_visible_toc_nodes.data + before, after = data.before, data.after + if before: + before = id_map[before] + if after: + after = id_map[after] + if before and after: + # Both border nodes are in the current spine item + return before, after + sb, sa = get_toc_nodes_bordering_spine_item(toc) + before = before or sb + after = after or sa + return before, after + + +def family_for_toc_node(toc_node_id, parent_map, id_map): + if not id_map or not parent_map: + toc = current_book().manifest.toc + parent_map, id_map = get_toc_maps(toc) + family = v'[]' + node = id_map[toc_node_id] + while node and node.title: + family.unshift(node) + parent = parent_map[node.id] + node = None + if parent: + node = id_map[parent.id] + return family + + def get_current_toc_nodes(): toc = current_book().manifest.toc parent_map, id_map = get_toc_maps(toc) @@ -78,16 +101,9 @@ def get_current_toc_nodes(): if before: ans[before.id] = True ans = Object.keys(ans) - family = v'[]' if ans.length: - node = id_map[ans[0]] - while node and node.title: - family.unshift(node) - parent = parent_map[node.id] - node = None - if parent: - node = id_map[parent.id] - return family + return family_for_toc_node(ans[0], parent_map, id_map) + return v'[]' def get_highlighted_toc_nodes(toc, parent_map, id_map): @@ -207,3 +223,27 @@ def update_visible_toc_anchors(toc_anchor_map, anchor_funcs): break return {'visible_anchors':visible_anchors, 'has_visible':has_visible, 'before':before, 'after':after, 'sorted_anchors':tam.sorted_anchors} + + +def find_anchor_before_range(r, toc_anchor_map, anchor_funcs): + name = current_spine_item().name + prev_anchor = None + tam = current_toc_anchor_map(toc_anchor_map, anchor_funcs) + anchors = toc_anchor_map[name] + if anchors: + amap = {x.id: x for x in anchors} + for anchor_id in tam.sorted_anchors: + anchor = amap[anchor_id] + is_before = True + if anchor.frag: + elem = document.getElementById(anchor.frag) + if elem: + q = document.createRange() + q.selectNode(elem) + if q.compareBoundaryPoints(window.Range.START_TO_START, r) > 0: + is_before = False + if is_before: + prev_anchor = anchor + else: + break + return prev_anchor