mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Display completion results
This commit is contained in:
parent
8943e2883c
commit
20bc198cb7
@ -12,7 +12,7 @@ from urlparse import urlparse
|
||||
|
||||
from PyQt5.Qt import (
|
||||
QObject, QApplication, QDialog, QGridLayout, QLabel, QSize, Qt,
|
||||
QDialogButtonBox, QIcon, QPixmap, QInputDialog, QUrl)
|
||||
QDialogButtonBox, QIcon, QPixmap, QInputDialog, QUrl, pyqtSignal)
|
||||
|
||||
from calibre import prints, isbytestring
|
||||
from calibre.ptempfile import PersistentTemporaryDirectory, TemporaryDirectory
|
||||
@ -69,6 +69,8 @@ def get_boss():
|
||||
|
||||
class Boss(QObject):
|
||||
|
||||
handle_completion_result_signal = pyqtSignal(object)
|
||||
|
||||
def __init__(self, parent, notify=None):
|
||||
global _boss
|
||||
QObject.__init__(self, parent)
|
||||
@ -83,7 +85,8 @@ class Boss(QObject):
|
||||
setup_cssutils_serialization()
|
||||
_boss = self
|
||||
self.gui = parent
|
||||
completion_worker().result_callback = self.handle_completion_result
|
||||
completion_worker().result_callback = self.handle_completion_result_signal.emit
|
||||
self.handle_completion_result_signal.connect(self.handle_completion_result, Qt.QueuedConnection)
|
||||
self.completion_request_count = 0
|
||||
|
||||
def __call__(self, gui):
|
||||
@ -676,6 +679,7 @@ class Boss(QObject):
|
||||
request_id = (self.completion_request_count, name)
|
||||
self.completion_request_count += 1
|
||||
completion_worker().queue_completion(request_id, completion_type, completion_data, query)
|
||||
return request_id[0]
|
||||
|
||||
def handle_completion_result(self, result):
|
||||
name = result.request_id[1]
|
||||
|
@ -7,7 +7,7 @@ __license__ = 'GPL v3'
|
||||
__copyright__ = '2014, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from threading import Event
|
||||
from collections import namedtuple
|
||||
from collections import namedtuple, OrderedDict
|
||||
|
||||
from PyQt5.Qt import QObject, pyqtSignal, Qt
|
||||
|
||||
@ -52,15 +52,21 @@ class Name(unicode):
|
||||
def complete_names(names_data, data_conn):
|
||||
if not names_cache:
|
||||
mime_map, spine_names = get_data(data_conn, 'names_data')
|
||||
names_cache[None] = frozenset(Name(name, mt, spine_names) for name, mt in mime_map.iteritems())
|
||||
names_cache['text_link'] = frozenset(n for n in names_cache if n.in_spine)
|
||||
names_cache['stylesheet'] = frozenset(n for n in names_cache if n.mime_type in OEB_STYLES)
|
||||
names_cache['image'] = frozenset(n for n in names_cache if n.mime_type.startswith('image/'))
|
||||
names_cache['font'] = frozenset(n for n in names_cache if n.mime_type in OEB_FONTS)
|
||||
names_cache[None] = all_names = frozenset(Name(name, mt, spine_names) for name, mt in mime_map.iteritems())
|
||||
names_cache['text_link'] = frozenset(n for n in all_names if n.in_spine)
|
||||
names_cache['stylesheet'] = frozenset(n for n in all_names if n.mime_type in OEB_STYLES)
|
||||
names_cache['image'] = frozenset(n for n in all_names if n.mime_type.startswith('image/'))
|
||||
names_cache['font'] = frozenset(n for n in all_names if n.mime_type in OEB_FONTS)
|
||||
names_cache['descriptions'] = d = {}
|
||||
for x, desc in {'text_link':_('Text'), 'stylesheet':_('Stylesheet'), 'image':_('Image'), 'font':_('Font')}.iteritems():
|
||||
for n in names_cache[x]:
|
||||
d[n] = desc
|
||||
names_type, base, root = names_data
|
||||
names = names_cache.get(names_type, names_cache[None])
|
||||
ans = frozenset(name_to_href(name, root, base) for name in names)
|
||||
return ans, {}
|
||||
nmap = {name:name_to_href(name, root, base) for name in names}
|
||||
items = frozenset(nmap.itervalues())
|
||||
descriptions = {href:names_cache.get(name) for name, href in nmap.iteritems()}
|
||||
return items, descriptions, {}
|
||||
|
||||
_current_matcher = (None, None, None)
|
||||
|
||||
@ -68,11 +74,15 @@ def handle_control_request(request, data_conn):
|
||||
global _current_matcher
|
||||
ans = control_funcs[request.type](request.data, data_conn)
|
||||
if ans is not None:
|
||||
items, matcher_kwargs = ans
|
||||
items, descriptions, matcher_kwargs = ans
|
||||
fingerprint = hash(items)
|
||||
if fingerprint != _current_matcher[0] or matcher_kwargs != _current_matcher[1]:
|
||||
_current_matcher = (fingerprint, matcher_kwargs, Matcher(items, **matcher_kwargs))
|
||||
ans = _current_matcher[-1](request.query or '', limit=50)
|
||||
if request.query:
|
||||
items = _current_matcher[-1](request.query, limit=50)
|
||||
else:
|
||||
items = OrderedDict((i, ()) for i in _current_matcher[-1].items)
|
||||
ans = items, descriptions
|
||||
return ans
|
||||
|
||||
class HandleDataRequest(QObject):
|
||||
|
@ -12,9 +12,10 @@ from math import ceil
|
||||
from PyQt5.Qt import (
|
||||
QWidget, Qt, QStaticText, QTextOption, QSize, QPainter, QTimer, QPen)
|
||||
|
||||
from calibre import prints
|
||||
from calibre.gui2 import error_dialog
|
||||
from calibre.gui2.tweak_book.widgets import make_highlighted_text
|
||||
from calibre.utils.icu import string_length
|
||||
from calibre.utils.matcher import Matcher
|
||||
|
||||
|
||||
class CompletionPopup(QWidget):
|
||||
@ -23,12 +24,13 @@ class CompletionPopup(QWidget):
|
||||
|
||||
def __init__(self, parent, max_height=1000):
|
||||
QWidget.__init__(self, parent)
|
||||
self.completion_error_shown = False
|
||||
self.setFocusPolicy(Qt.NoFocus)
|
||||
self.setFocusProxy(parent)
|
||||
self.setVisible(False)
|
||||
|
||||
self.matcher = None
|
||||
self.current_query = self.current_results = self.current_size_hint = None
|
||||
self.current_results = self.current_size_hint = None
|
||||
self.max_text_length = 0
|
||||
self.current_index = -1
|
||||
self.current_top_index = 0
|
||||
@ -47,25 +49,11 @@ class CompletionPopup(QWidget):
|
||||
self.rendered_text_cache.clear()
|
||||
self.current_size_hint = None
|
||||
|
||||
def set_items(self, items, items_are_filenames=False):
|
||||
kw = {}
|
||||
if not items_are_filenames:
|
||||
kw['level1'] = ' '
|
||||
self.matcher = Matcher(tuple(items), **kw)
|
||||
self.descriptions = dict(items) if isinstance(items, dict) else {}
|
||||
self.clear_caches()
|
||||
self.set_query()
|
||||
|
||||
def set_query(self, query='', limit=100):
|
||||
def set_items(self, items, descriptions=None):
|
||||
self.current_results = tuple(items.iteritems())
|
||||
self.current_size_hint = None
|
||||
self.current_query = query
|
||||
if self.matcher is None:
|
||||
self.current_results = ()
|
||||
else:
|
||||
if query:
|
||||
self.current_results = tuple(self.matcher(query, limit=limit).iteritems())
|
||||
else:
|
||||
self.current_results = tuple((text, ()) for text in self.matcher.items[:limit])
|
||||
self.descriptions = descriptions or {}
|
||||
self.clear_caches()
|
||||
self.max_text_length = 0
|
||||
self.current_index = -1
|
||||
self.current_top_index = 0
|
||||
@ -94,7 +82,7 @@ class CompletionPopup(QWidget):
|
||||
sz = self.get_static_text(text, positions).size()
|
||||
height += int(ceil(sz.height())) + self.TOP_MARGIN + self.BOTTOM_MARGIN
|
||||
max_width = max(max_width, int(ceil(sz.width())))
|
||||
self.current_size_hint = QSize(max_width, height)
|
||||
self.current_size_hint = QSize(max_width, height + 2)
|
||||
return self.current_size_hint
|
||||
|
||||
def iter_visible_items(self):
|
||||
@ -184,6 +172,26 @@ class CompletionPopup(QWidget):
|
||||
QWidget.hide(self)
|
||||
self.relayout_timer.stop()
|
||||
|
||||
def handle_result(self, result):
|
||||
if result.traceback:
|
||||
prints(result.traceback)
|
||||
if not self.completion_error_shown:
|
||||
error_dialog(self, _('Completion failed'), _(
|
||||
'Failed to get completions, click "Show Details" for more information.'
|
||||
' Future errors during completion will be suppressed.'), det_msg=result.traceback, show=True)
|
||||
self.completion_error_shown = True
|
||||
self.hide()
|
||||
return
|
||||
if result.ans is None:
|
||||
self.hide()
|
||||
return
|
||||
items, descriptions = result.ans
|
||||
if not items:
|
||||
self.hide()
|
||||
return
|
||||
self.set_items(items, descriptions)
|
||||
self.show()
|
||||
|
||||
def handle_keypress(self, ev):
|
||||
key = ev.key()
|
||||
if key == Qt.Key_Escape:
|
||||
@ -214,9 +222,11 @@ class CompletionPopup(QWidget):
|
||||
return False
|
||||
|
||||
if __name__ == '__main__':
|
||||
from calibre.utils.matcher import Matcher
|
||||
def test(editor):
|
||||
c = editor.__c = CompletionPopup(editor.editor, max_height=100)
|
||||
c.set_items('one two three four five six seven eight nine ten'.split())
|
||||
m = Matcher('one two three four five six seven eight nine ten'.split())
|
||||
c.set_items(m('one'))
|
||||
QTimer.singleShot(10, c.show)
|
||||
from calibre.gui2.tweak_book.editor.widget import launch_editor
|
||||
raw = textwrap.dedent('''\
|
||||
|
@ -288,7 +288,7 @@ class Smarts(NullSmarts):
|
||||
Smarts.closing_tag_pat = re.compile(r'<\s*/[^>]+>')
|
||||
Smarts.closing_pat = re.compile(r'<\s*/')
|
||||
Smarts.self_closing_pat = re.compile(r'/\s*>')
|
||||
Smarts.complete_attr_pat = re.compile(r'''([a-zA-Z0-9_-]+)\s*=\s*(?:'([^']+)|"([^"]+))$''')
|
||||
Smarts.complete_attr_pat = re.compile(r'''([a-zA-Z0-9_-]+)\s*=\s*(?:'([^']*)|"([^"]*))$''')
|
||||
NullSmarts.__init__(self, *args, **kwargs)
|
||||
self.last_matched_tag = None
|
||||
|
||||
@ -644,7 +644,7 @@ class Smarts(NullSmarts):
|
||||
doc_name = editor.highlighter.doc_name
|
||||
if doc_name and attr in {'href', 'src'}:
|
||||
# A link
|
||||
query = m.group(2) or m.group(3)
|
||||
query = m.group(2) or m.group(3) or ''
|
||||
names_type = {'a':'text_link', 'img':'image', 'image':'image', 'link':'stylesheet'}.get(tagname)
|
||||
return 'complete_names', (names_type, doc_name, current_container().root), query
|
||||
|
||||
|
@ -141,6 +141,7 @@ class TextEdit(PlainTextEdit):
|
||||
PlainTextEdit.__init__(self, parent)
|
||||
self.completion_popup = CompletionPopup(self)
|
||||
self.request_completion = self.completion_doc_name = None
|
||||
self.last_completion_request = -1
|
||||
self.gutter_width = 0
|
||||
self.tw = 2
|
||||
self.expected_geometry = expected_geometry
|
||||
@ -782,11 +783,14 @@ class TextEdit(PlainTextEdit):
|
||||
return
|
||||
result = self.smarts.get_completion_data(self, ev)
|
||||
if result is None:
|
||||
return
|
||||
self.request_completion(*result)
|
||||
self.last_completion_request += 1
|
||||
self.completion_popup.hide()
|
||||
else:
|
||||
self.last_completion_request = self.request_completion(*result)
|
||||
|
||||
def handle_completion_result(self, result):
|
||||
print (result)
|
||||
if result.request_id[0] >= self.last_completion_request:
|
||||
self.completion_popup.handle_result(result)
|
||||
|
||||
def replace_possible_unicode_sequence(self):
|
||||
c = self.textCursor()
|
||||
|
Loading…
x
Reference in New Issue
Block a user