mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Viewer: When the search results panel is expanded, show more context
This commit is contained in:
parent
b0e427621a
commit
d4b5fbeb28
@ -5,12 +5,13 @@
|
|||||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
from collections import Counter, OrderedDict
|
from collections import Counter, OrderedDict
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
|
||||||
import regex
|
import regex
|
||||||
from PyQt5.Qt import (
|
from PyQt5.Qt import (
|
||||||
QAction, QCheckBox, QComboBox, QFont, QHBoxLayout, QIcon, QLabel, QStaticText,
|
QAction, QCheckBox, QComboBox, QFont, QFontMetrics, QHBoxLayout, QIcon, QLabel,
|
||||||
QStyle, QStyledItemDelegate, Qt, QToolButton, QTreeWidget, QTreeWidgetItem,
|
QStyle, QStyledItemDelegate, Qt, QToolButton, QTreeWidget, QTreeWidgetItem,
|
||||||
QVBoxLayout, QWidget, pyqtSignal
|
QVBoxLayout, QWidget, pyqtSignal
|
||||||
)
|
)
|
||||||
@ -127,8 +128,7 @@ class SearchResult(object):
|
|||||||
|
|
||||||
__slots__ = (
|
__slots__ = (
|
||||||
'search_query', 'before', 'text', 'after', 'q', 'spine_idx',
|
'search_query', 'before', 'text', 'after', 'q', 'spine_idx',
|
||||||
'index', 'file_name', '_static_text', 'is_hidden', 'offset',
|
'index', 'file_name', 'is_hidden', 'offset', 'toc_nodes'
|
||||||
'toc_nodes'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(self, search_query, before, text, after, q, name, spine_idx, index, offset):
|
def __init__(self, search_query, before, text, after, q, name, spine_idx, index, offset):
|
||||||
@ -137,7 +137,6 @@ class SearchResult(object):
|
|||||||
self.before, self.text, self.after = before, text, after
|
self.before, self.text, self.after = before, text, after
|
||||||
self.spine_idx, self.index = spine_idx, index
|
self.spine_idx, self.index = spine_idx, index
|
||||||
self.file_name = name
|
self.file_name = name
|
||||||
self._static_text = None
|
|
||||||
self.is_hidden = False
|
self.is_hidden = False
|
||||||
self.offset = offset
|
self.offset = offset
|
||||||
try:
|
try:
|
||||||
@ -147,24 +146,6 @@ class SearchResult(object):
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
self.toc_nodes = ()
|
self.toc_nodes = ()
|
||||||
|
|
||||||
@property
|
|
||||||
def static_text(self):
|
|
||||||
if self._static_text is None:
|
|
||||||
before_words = self.before.split()
|
|
||||||
before = ' '.join(before_words[-3:])
|
|
||||||
before_extra = len(before) - 15
|
|
||||||
if before_extra > 0:
|
|
||||||
before = before[before_extra:]
|
|
||||||
before = '…' + before
|
|
||||||
before_space = '' if self.before.rstrip() == self.before else ' '
|
|
||||||
after_words = self.after.split()
|
|
||||||
after = ' '.join(after_words[:3])[:15] + '…'
|
|
||||||
after_space = '' if self.after.lstrip() == self.after else ' '
|
|
||||||
self._static_text = st = QStaticText('<p>{}{}<b>{}</b>{}{}'.format(before, before_space, self.text, after_space, after))
|
|
||||||
st.setTextFormat(Qt.RichText)
|
|
||||||
st.setTextWidth(10000)
|
|
||||||
return self._static_text
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def for_js(self):
|
def for_js(self):
|
||||||
return {
|
return {
|
||||||
@ -464,24 +445,76 @@ class ResultsDelegate(QStyledItemDelegate): # {{{
|
|||||||
if not isinstance(result, SearchResult):
|
if not isinstance(result, SearchResult):
|
||||||
return
|
return
|
||||||
painter.save()
|
painter.save()
|
||||||
p = option.palette
|
|
||||||
c = p.HighlightedText if option.state & QStyle.State_Selected else p.Text
|
|
||||||
group = (p.Active if option.state & QStyle.State_Active else p.Inactive)
|
|
||||||
c = p.color(group, c)
|
|
||||||
painter.setClipRect(option.rect)
|
|
||||||
painter.setPen(c)
|
|
||||||
height = result.static_text.size().height()
|
|
||||||
tl = option.rect.topLeft()
|
|
||||||
x, y = tl.x(), tl.y()
|
|
||||||
y += (option.rect.height() - height) // 2
|
|
||||||
if result.is_hidden:
|
|
||||||
x += option.decorationSize.width() + 4
|
|
||||||
try:
|
try:
|
||||||
painter.drawStaticText(x, y, result.static_text)
|
p = option.palette
|
||||||
|
c = p.HighlightedText if option.state & QStyle.State_Selected else p.Text
|
||||||
|
group = (p.Active if option.state & QStyle.State_Active else p.Inactive)
|
||||||
|
c = p.color(group, c)
|
||||||
|
painter.setPen(c)
|
||||||
|
font = option.font
|
||||||
|
emphasis_font = QFont(font)
|
||||||
|
emphasis_font.setBold(True)
|
||||||
|
flags = Qt.AlignTop | Qt.TextSingleLine | Qt.TextIncludeTrailingSpaces
|
||||||
|
rect = option.rect.adjusted(option.decorationSize.width() + 4 if result.is_hidden else 0, 0, 0, 0)
|
||||||
|
painter.setClipRect(rect)
|
||||||
|
before = re.sub(r'\s+', ' ', result.before)
|
||||||
|
before_width = 0
|
||||||
|
if before:
|
||||||
|
before_width = painter.boundingRect(rect, flags, before).width()
|
||||||
|
after = re.sub(r'\s+', ' ', result.after.rstrip())
|
||||||
|
after_width = 0
|
||||||
|
if after:
|
||||||
|
after_width = painter.boundingRect(rect, flags, after).width()
|
||||||
|
ellipsis_width = painter.boundingRect(rect, flags, '...').width()
|
||||||
|
painter.setFont(emphasis_font)
|
||||||
|
text = re.sub(r'\s+', ' ', result.text)
|
||||||
|
match_width = painter.boundingRect(rect, flags, text).width()
|
||||||
|
if match_width >= rect.width() - 3 * ellipsis_width:
|
||||||
|
efm = QFontMetrics(emphasis_font)
|
||||||
|
text = efm.elidedText(text, Qt.ElideRight, rect.width())
|
||||||
|
painter.drawText(rect, flags, text)
|
||||||
|
else:
|
||||||
|
self.draw_match(
|
||||||
|
painter, flags, before, text, after, rect, before_width, match_width, after_width, ellipsis_width, emphasis_font, font)
|
||||||
except Exception:
|
except Exception:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
painter.restore()
|
painter.restore()
|
||||||
|
|
||||||
|
def draw_match(self, painter, flags, before, text, after, rect, before_width, match_width, after_width, ellipsis_width, emphasis_font, normal_font):
|
||||||
|
extra_width = int(rect.width() - match_width)
|
||||||
|
if before_width < after_width:
|
||||||
|
left_width = min(extra_width // 2, before_width)
|
||||||
|
right_width = extra_width - left_width
|
||||||
|
else:
|
||||||
|
right_width = min(extra_width // 2, after_width)
|
||||||
|
left_width = min(before_width, extra_width - right_width)
|
||||||
|
x = rect.left()
|
||||||
|
nfm = QFontMetrics(normal_font)
|
||||||
|
if before_width and left_width:
|
||||||
|
r = rect.adjusted(0, 0, 0, 0)
|
||||||
|
r.setRight(x + left_width)
|
||||||
|
painter.setFont(normal_font)
|
||||||
|
ebefore = nfm.elidedText(before, Qt.ElideLeft, left_width)
|
||||||
|
if ebefore == before:
|
||||||
|
ebefore = '…' + before[1:]
|
||||||
|
r.setLeft(x)
|
||||||
|
x += painter.drawText(r, flags, ebefore).width()
|
||||||
|
painter.setFont(emphasis_font)
|
||||||
|
r = rect.adjusted(0, 0, 0, 0)
|
||||||
|
r.setLeft(x)
|
||||||
|
painter.drawText(r, flags, text).width()
|
||||||
|
x += match_width
|
||||||
|
if after_width and right_width:
|
||||||
|
painter.setFont(normal_font)
|
||||||
|
r = rect.adjusted(0, 0, 0, 0)
|
||||||
|
r.setLeft(x)
|
||||||
|
eafter = nfm.elidedText(after, Qt.ElideRight, right_width)
|
||||||
|
if eafter == after:
|
||||||
|
eafter = after[:-1] + '…'
|
||||||
|
painter.setFont(normal_font)
|
||||||
|
painter.drawText(r, flags, eafter)
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
@ -571,7 +604,7 @@ class Results(QTreeWidget): # {{{
|
|||||||
r = item.data(0, Qt.UserRole)
|
r = item.data(0, Qt.UserRole)
|
||||||
if r.is_result(sr):
|
if r.is_result(sr):
|
||||||
r.is_hidden = True
|
r.is_hidden = True
|
||||||
item.setIcon(self.not_found_icon)
|
item.setIcon(0, self.not_found_icon)
|
||||||
break
|
break
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
Loading…
x
Reference in New Issue
Block a user