calibre/src/pyj/read_book/hints.pyj
2020-12-15 22:26:26 +05:30

165 lines
5.2 KiB
Plaintext

# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import bound_methods, hash_literals
from dom import clear
from elementmaker import E
from book_list.theme import get_color
from gettext import gettext as _
from read_book.shortcuts import shortcut_for_key_event
class Hints:
def __init__(self, view):
self.view = view
container = self.container
container.setAttribute('tabindex', '0')
container.style.overflow = 'hidden'
container.addEventListener('keydown', self.on_keydown, {'passive': False})
container.addEventListener('click', self.container_clicked, {'passive': False})
self.reset()
def reset(self):
self.hints_map = {}
self.current_prefix = ''
@property
def container(self):
return document.getElementById('book-hints-overlay')
@property
def is_visible(self):
return self.container.style.display is not 'none'
def focus(self):
self.container.focus()
def hide(self):
if self.is_visible:
self.container.style.display = 'none'
self.send_message('hide')
self.reset()
self.view.focus_iframe()
def show(self):
if not self.is_visible:
self.reset()
c = self.container
c.style.display = 'block'
clear(c)
self.focus()
self.send_message('show')
def on_keydown(self, ev):
ev.preventDefault(), ev.stopPropagation()
if ev.key is 'Escape':
self.hide()
return
if ev.key is 'Enter':
if self.current_prefix:
self.apply_prefix(True)
return
if ev.key is 'Backspace':
if self.current_prefix:
self.current_prefix = self.current_prefix[:-1]
self.apply_prefix()
return
hint_keys = list('1234567890abcdefghijklmnopqrstuvwxyz')
q = ev.key.toLowerCase()
if hint_keys.indexOf(q) > -1:
self.current_prefix += q
self.apply_prefix()
sc_name = shortcut_for_key_event(ev, self.view.keyboard_shortcut_map)
if not sc_name:
return
def container_clicked(self, ev):
ev.stopPropagation(), ev.preventDefault()
self.hide()
def apply_prefix(self, accept_full_match):
matches = v'[]'
if self.current_prefix:
for k in Object.keys(self.hints_map):
if k is '_length':
continue
q = encode(int(k))
if accept_full_match:
if q is self.current_prefix:
matches.push(k)
break
elif q.startswith(self.current_prefix):
matches.push(k)
if matches.length is 1:
self.send_message('activate', hint=self.hints_map[matches[0]])
self.hide()
else:
self.send_message('apply_prefix', prefix=self.current_prefix)
def send_message(self, type, **kw):
self.view.iframe_wrapper.send_message('hints', type=type, **kw)
def handle_message(self, msg):
if msg.type is 'shown':
self.reset()
self.hints_map = msg.hints_map
if not self.hints_map._length:
self.no_hints_found()
def no_hints_found(self):
c = self.container
c.appendChild(E.div(
_('No links found. Press Esc to close'),
style=f'position: absolute; margin: auto; top: 50%; left: 50%; background: {get_color("window-background")};' +
' padding: 1rem; border: solid 1px currentColor; border-radius: 4px; transform: translate(-50%, -50%);'
))
def is_visible(a):
if not a.offsetParent:
return False
rect = a.getBoundingClientRect()
return rect.left >= 0 and rect.top >= 0 and rect.left < window.innerWidth and rect.top < window.innerHeight
def encode(i):
return i.toString(36).toLowerCase()
def hint_visible_links():
i = 0
hint_map = {}
for a in document.body.querySelectorAll('a[href]'):
if is_visible(a):
i += 1
h = i + ''
a.dataset.calibreHintRender = encode(i)
a.dataset.calibreHintValue = h
a.classList.add('calibre-hint-visible')
hint_map[h] = {'type': 'link', 'value': i}
hint_map._length = i
return hint_map
def unhint_links():
for a in document.body.querySelectorAll('a[href]'):
a.classList.remove('calibre-hint-visible', 'calibre-hint-enter')
v'delete a.dataset.calibreHintRender'
v'delete a.dataset.calibreHintValue'
def apply_prefix_to_hints(prefix):
for a in document.body.querySelectorAll('[data-calibre-hint-value]'):
val = int(a.dataset.calibreHintValue)
r = encode(val)
a.classList.remove('calibre-hint-enter')
if not prefix or r.startsWith(prefix):
a.classList.add('calibre-hint-visible')
a.dataset.calibreHintRender = leftover = r[prefix.length:] or '\xa0'
if leftover is '\xa0':
a.classList.add('calibre-hint-enter')
else:
a.classList.remove('calibre-hint-visible')