Simplify text marking in completion.

Also improves performance, see #190.
This commit is contained in:
Florian Bruhin 2014-10-20 07:43:07 +02:00
parent 1917911dd8
commit 0a5cee6ea2
5 changed files with 12 additions and 76 deletions

View File

@ -29,8 +29,7 @@ from PyQt5.QtGui import QStandardItemModel, QStandardItem
from qutebrowser.utils import usertypes, qtutils from qutebrowser.utils import usertypes, qtutils
Role = usertypes.enum('Role', ['marks', 'sort'], start=Qt.UserRole, Role = usertypes.enum('Role', ['sort'], start=Qt.UserRole, is_int=True)
is_int=True)
class BaseCompletionModel(QStandardItemModel): class BaseCompletionModel(QStandardItemModel):
@ -45,44 +44,6 @@ class BaseCompletionModel(QStandardItemModel):
super().__init__(parent) super().__init__(parent)
self.setColumnCount(3) self.setColumnCount(3)
def _get_marks(self, needle, haystack):
"""Return the marks for needle in haystack.
Args:
needle: The substring which should match.
haystack: The string where the matches should be in.
Return:
A list of (startidx, endidx) tuples.
"""
pos1 = pos2 = 0
marks = []
if not needle:
return marks
needle = needle.casefold()
haystack = haystack.casefold()
while True:
pos1 = haystack.find(needle, pos2)
if pos1 == -1:
break
pos2 = pos1 + len(needle)
marks.append((pos1, pos2))
return marks
def mark_item(self, index, needle):
"""Mark a string in the givem item.
Args:
index: A QModelIndex of the item to mark.
needle: The string to mark.
"""
qtutils.ensure_valid(index)
haystack = self.data(index)
marks = self._get_marks(needle, haystack)
ok = self.setData(index, marks, Role.marks)
if not ok:
raise ValueError("Error while setting data!")
def new_category(self, name, sort=None): def new_category(self, name, sort=None):
"""Add a new category to the model. """Add a new category to the model.

View File

@ -100,20 +100,6 @@ class CompletionFilterModel(QSortFilterProxyModel):
return index return index
return QModelIndex() return QModelIndex()
def mark_all_items(self, text):
"""Mark the given text in all visible items."""
if not text:
return
for i in range(self.rowCount()):
cat = self.index(i, 0)
qtutils.ensure_valid(cat)
for k in range(self.rowCount(cat)):
index = self.index(k, 0, cat)
qtutils.ensure_valid(index)
index = self.mapToSource(index)
qtutils.ensure_valid(index)
self.srcmodel.mark_item(index, text)
def setSourceModel(self, model): def setSourceModel(self, model):
"""Override QSortFilterProxyModel's setSourceModel to clear pattern.""" """Override QSortFilterProxyModel's setSourceModel to clear pattern."""
log.completion.debug("Setting source model: {}".format(model)) log.completion.debug("Setting source model: {}".format(model))

View File

@ -253,6 +253,5 @@ class Completer(QObject):
completion.hide() completion.hide()
return return
self._model().mark_all_items(pattern)
if completion.enabled: if completion.enabled:
completion.show() completion.show()

View File

@ -39,8 +39,6 @@ class CompletionView(QTreeView):
Based on QTreeView but heavily customized so root elements show as category Based on QTreeView but heavily customized so root elements show as category
headers, and children show as flat list. headers, and children show as flat list.
Highlights completions based on marks in the Role.marks data.
Class attributes: Class attributes:
COLUMN_WIDTHS: A list of column widths, in percent. COLUMN_WIDTHS: A list of column widths, in percent.

View File

@ -184,10 +184,6 @@ class CompletionItemDelegate(QStyledItemDelegate):
self._opt.direction, self._opt.displayAlignment)) self._opt.direction, self._opt.displayAlignment))
self._doc = QTextDocument(self) self._doc = QTextDocument(self)
if index.parent().isValid():
self._doc.setPlainText(self._opt.text)
else:
self._doc.setHtml('<b>{}</b>'.format(html.escape(self._opt.text)))
self._doc.setDefaultFont(self._opt.font) self._doc.setDefaultFont(self._opt.font)
self._doc.setDefaultTextOption(text_option) self._doc.setDefaultTextOption(text_option)
self._doc.setDefaultStyleSheet(style.get_stylesheet(""" self._doc.setDefaultStyleSheet(style.get_stylesheet("""
@ -197,21 +193,17 @@ class CompletionItemDelegate(QStyledItemDelegate):
""")) """))
self._doc.setDocumentMargin(2) self._doc.setDocumentMargin(2)
if index.column() == 0 and index.model().pattern: if index.parent().isValid():
# We take a shortcut here by not checking anything if the models pattern = index.model().pattern
# pattern is empty. This is required because the marks data is if index.column() == 0 and pattern:
# invalid in that case (for performance reasons). text = self._opt.text.replace(
marks = index.data(basecompletion.Role.marks) pattern,
if marks is None: '<span class="highlight">{}</span>'.format(pattern))
return self._doc.setHtml(text)
for mark in marks: else:
cur = QTextCursor(self._doc) self._doc.setPlainText(self._opt.text)
cur.setPosition(mark[0]) else:
cur.setPosition(mark[1], QTextCursor.KeepAnchor) self._doc.setHtml('<b>{}</b>'.format(html.escape(self._opt.text)))
txt = cur.selectedText()
cur.removeSelectedText()
cur.insertHtml('<span class="highlight">{}</span>'.format(
html.escape(txt)))
def _draw_focus_rect(self): def _draw_focus_rect(self):
"""Draw the focus rectangle of an ItemViewItem.""" """Draw the focus rectangle of an ItemViewItem."""