From 48ba6080ea9a37809680e0a3a55f34cd909fde5f Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 Jun 2016 08:06:03 +0530 Subject: [PATCH] Implement searching the TOC view --- src/pyj/book_list/theme.pyj | 3 +++ src/pyj/read_book/toc.pyj | 38 +++++++++++++++++++++++++- src/pyj/widgets.pyj | 53 ++++++++++++++++++++++++++++++++++--- 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/src/pyj/book_list/theme.pyj b/src/pyj/book_list/theme.pyj index 49956bb6c6..c44664e293 100644 --- a/src/pyj/book_list/theme.pyj +++ b/src/pyj/book_list/theme.pyj @@ -24,6 +24,9 @@ def get_color(name): 'list-hover-background': DARK, 'list-hover-foreground': LIGHT, + # Tree colors + 'tree-highlight-item': LIGHT_DARKER, + # Button colors 'button-start': DARK, 'button-end': '#49423B', diff --git a/src/pyj/read_book/toc.pyj b/src/pyj/read_book/toc.pyj index 5e5fadaa8b..5494aca0b3 100644 --- a/src/pyj/read_book/toc.pyj +++ b/src/pyj/read_book/toc.pyj @@ -5,7 +5,8 @@ from __python__ import hash_literals from dom import set_css, svgicon from elementmaker import E from gettext import gettext as _ -from widgets import create_tree +from modals import error_dialog +from widgets import create_tree, find_text_in_tree, scroll_tree_item_into_view def create_toc_tree(toc, onclick): @@ -16,6 +17,30 @@ def create_toc_tree(toc, onclick): return create_tree(toc, populate_data, onclick) +def do_search(text, container): + a = find_text_in_tree(container, text) + if not text: + return + if not a: + return error_dialog(_('No matches found'), _( + 'The text "{}" was not found in the Table of Contents').format(text)) + scroll_tree_item_into_view(a) + +def on_input_keydown(event): + if event.keyCode is 13: # Enter + event.preventDefault(), event.stopPropagation() + text = event.target.value + event.target.nextSibling.focus() + container = event.target.parentNode.parentNode.firstChild.nextSibling + do_search(text, container) + +def on_search_click(event): + if event.button is 0: + event.preventDefault(), event.stopPropagation() + text = event.currentTarget.previousSibling.value + container = event.currentTarget.parentNode.parentNode.firstChild.nextSibling + do_search(text, container) + def create_toc_panel(book, container, onclick, onclose): container.appendChild(E.div( style='display: flex; justify-content: space-between; padding: 1ex 1em; border-bottom: solid 1px currentColor', @@ -29,3 +54,14 @@ def create_toc_panel(book, container, onclick, onclose): set_css(container, display='flex', flex_direction='column') set_css(toc_panel, flex_grow='10') container.appendChild(toc_panel) + + container.appendChild(E.div( + style='margin: 1ex 1em; display: flex;', + E.input( + type='search', autosave='search-toc-in-calibre-book-reader', name='toc-serach', + autocomplete='on', inputmode='latin', + title=_('Search Table of Contents'), placeholder=_('Search Table of Contents'), spellcheck='false', + style="flex-grow: 10; margin-right: 0.5em", onkeydown=on_input_keydown, + ), + E.div(class_='simple-link', svgicon('search'), onclick=on_search_click) + )) diff --git a/src/pyj/widgets.pyj b/src/pyj/widgets.pyj index 9fb33ffa52..749c713865 100644 --- a/src/pyj/widgets.pyj +++ b/src/pyj/widgets.pyj @@ -130,7 +130,7 @@ def create_tree(root, populate_data, onclick): E.span('\xa0'), E.a( href='javascript: void(0)', - class_='simple-link', + class_='simple-link tree-item-title', onclick=def (event): if onclick: if event.button is 0: @@ -138,7 +138,7 @@ def create_tree(root, populate_data, onclick): onclick(event, event.currentTarget.parentNode.parentNode) ), ), - E.div(style='display:none'), + E.div(style='display:none', data_tree_subtree_container='1'), data_tree_state='closed', ) ul.appendChild(li) @@ -153,11 +153,58 @@ def create_tree(root, populate_data, onclick): if root: process_node(root, container, 0) return container + +def find_text_in_tree(container, q): + q = q.lower() + last_match = container.querySelector('a[data-tree-last-match]') + if last_match: + last_match.parentNode.style.backgroundColor = 'transparent' + last_match.parentNode.style.borderRadius = '0' + lm = last_match.getAttribute('data-tree-last-match') + last_match.removeAttribute('data-tree-last-match') + if lm is not q: + last_match = None + if not q: + return + before = [] + seen = False + ans = None + + for a in container.querySelectorAll('a.tree-item-title'): + if a is last_match: + seen = True + else: + if a.textContent.lower().indexOf(q) != -1: + if seen: + ans = a + break + if last_match is None: + ans = a + break + before.push(a) + if not ans and before.length: + ans = before[0] + ans = ans or last_match + if ans: + ans.dataset.treeLastMatch = q + if ans: + ans.parentNode.style.backgroundColor = get_color('tree-highlight-item') + ans.parentNode.style.borderRadius = '5px' + ans = ans.parentNode.parentNode + return ans + +def scroll_tree_item_into_view(item): + p = item.parentNode?.parentNode + while p and p.getAttribute('data-tree-subtree-container'): + p.style.display = 'block' + p = p.parentNode?.parentNode?.parentNode + item.scrollIntoView() + # }}} def get_widget_css(): ans = 'a, button:focus { outline: none }; a, button::-moz-focus-inner { border: 0 }\n' - ans += '.simple-link { cursor: pointer } .simple-link:hover { color: red } .simple-tree:active { transform: scale(1.5) }\n' + ans += '.simple-link { cursor: pointer } .simple-link:hover { color: red } .simple-link:active { transform: scale(1.5) }\n' ans += create_button.style ans += create_spinner.style ans += Breadcrumbs.STYLE_RULES