calibre/src/pyj/complete.pyj
Kovid Goyal 5635216999
...
2022-07-06 19:19:54 +05:30

187 lines
7.0 KiB
Plaintext

# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import hash_literals, bound_methods
from dom import ensure_id, unique_id, clear
from elementmaker import E
from session import local_storage
from popups import CompletionPopup
from utils import uniq
class EditWithComplete:
def __init__(self, name, placeholder=None, tooltip=None, parent=None, input_type='text', onenterkey=None):
inpt = E.input(type=input_type, name=name, title=tooltip or '', placeholder=placeholder or '', autocomplete='off')
self.input_id = ensure_id(inpt)
self.onenterkey = onenterkey
self.completion_popup = CompletionPopup(parent=parent, onselect=self.apply_completion)
inpt.addEventListener('keydown', self.onkeydown)
inpt.addEventListener('input', self.oninput)
self.completion_popup.add_associated_widget(inpt)
if parent:
parent.appendChild(inpt)
@property
def text_input(self):
return document.getElementById(self.input_id)
def apply_completion(self, text):
if text:
ti = self.text_input
ti.value = text
ti.focus()
return True
def onkeydown(self, event):
if self.completion_popup.is_visible and self.completion_popup.handle_keydown(event):
event.preventDefault(), event.stopPropagation()
return
if event.key is 'Enter':
if self.completion_popup.is_visible:
if self.apply_completion(self.completion_popup.current_text):
self.completion_popup.hide()
event.preventDefault(), event.stopPropagation()
return
if self.onenterkey:
event.preventDefault(), event.stopPropagation()
self.onenterkey()
elif event.key is 'Tab':
if self.completion_popup.is_visible:
if self.apply_completion(self.completion_popup.current_text):
self.completion_popup.hide()
else:
self.completion_popup.move_highlight()
event.preventDefault(), event.stopPropagation()
elif event.key is 'ArrowDown':
ti = self.text_input
if not ti.value:
self.completion_popup.set_query('')
self.completion_popup.popup(ti)
event.preventDefault(), event.stopPropagation()
def oninput(self, event):
ti = self.text_input
self.completion_popup.set_query(ti.value or '')
self.completion_popup.popup(ti)
def hide_completion_popup(self):
self.completion_popup.hide()
def add_associated_widget(self, widget_or_id):
self.completion_popup.add_associated_widget(widget_or_id)
def set_all_items(self, items):
self.completion_popup.set_all_items(items)
def create_search_bar(action, name, tooltip=None, placeholder=None, button=None, history_size=100, associated_widgets=None):
parent = E.div(style="display:flex")
ewc = EditWithComplete(name, parent=parent, tooltip=tooltip, placeholder=placeholder, input_type='search')
parent.lastChild.style.width = '100%'
history_name = 'search-bar-history-' + name
def update_completion_items():
items = local_storage().get(history_name)
if items?.length:
ewc.set_all_items(items)
update_completion_items()
def trigger():
text = ewc.text_input.value
ewc.hide_completion_popup()
action(text)
if text and text.strip():
items = local_storage().get(history_name) or v'[]'
idx = items.indexOf(text)
if idx > -1:
items.splice(idx, 1)
items.unshift(text)
local_storage().set(history_name, uniq(items[:history_size]))
update_completion_items()
ewc.onenterkey = trigger
parent.querySelector('input').addEventListener('search', trigger)
if button:
ewc.add_associated_widget(button)
button.addEventListener('click', trigger)
if associated_widgets is not None:
for w in associated_widgets:
ewc.add_associated_widget(w)
return parent
def create_simple_input_with_history_native(name, tooltip=None, placeholder=None, history_size=100, input_type='text'):
# cannot use in standalone viewer because of: https://bugreports.qt.io/browse/QTBUG-54433
# fixed in Qt 6.4.0
dl_id = unique_id()
dl = document.createElement('datalist')
dl.id = dl_id
history_name = f'simple_input_history_{name}'
def update_completion_items(x):
items = local_storage().get(history_name)
dl = x or document.getElementById(dl_id)
if dl:
clear(dl)
if items?.length:
for item in items:
dl.appendChild(E.option(value=item))
def save_completion_items():
text = document.getElementById(dl_id).nextSibling.value
if text and text.strip():
items = local_storage().get(history_name) or v'[]'
idx = items.indexOf(text)
if idx > -1:
items.splice(idx, 1)
items.unshift(text)
local_storage().set(history_name, uniq(items[:history_size]))
update_completion_items()
update_completion_items(dl)
ans = E.span(
data_calibre_history_input='1',
dl,
E.input(type=input_type, name=name, list=dl_id, title=tooltip or '', placeholder=placeholder or ''),
)
ans.addEventListener('save_history', save_completion_items)
return ans
def create_simple_input_with_history(name, tooltip=None, placeholder=None, history_size=100, input_type='text'):
# to use simply add the returned element to the DOM and when you want to
# save a new completion item, use dispatchEvent to send a 'save_history'
# event to it.
parent = E.span(data_calibre_history_input='1')
ewc = EditWithComplete(name, parent=parent, tooltip=tooltip, placeholder=placeholder, input_type=input_type)
history_name = f'simple_input_history_{name}'
def update_completion_items():
items = local_storage().get(history_name)
if items?.length:
ewc.set_all_items(items)
update_completion_items()
def save_completion_items():
text = ewc.text_input.value
if text and text.strip():
items = local_storage().get(history_name) or v'[]'
idx = items.indexOf(text)
if idx > -1:
items.splice(idx, 1)
items.unshift(text)
local_storage().set(history_name, uniq(items[:history_size]))
update_completion_items()
parent.addEventListener('save_history', save_completion_items)
return parent
# }}}
def main(container):
container.appendChild(create_search_bar(print, 'test-search-bar', placeholder='Testing search bar'))
container.firstChild.lastChild.focus()
# ewc = EditWithComplete(parent=container, placeholder='Testing edit with complete')
# ewc.set_all_items('a a1 a11 a12 a13 b b1 b2 b3'.split(' '))
# ewc.text_input.focus()