29#include <QApplication>
32#include <QtGuiVersion>
35 : QStyledItemDelegate(parent)
43 QStyleOptionViewItem opt = option;
44 initStyleOption(&opt, index);
46 const QWidget* w = opt.widget;
47 const QStyle* style = w ? w->style() : QApplication::style();
49 const int height = opt.rect.height();
50 const int center = height / 2 + opt.rect.top();
53 QFont linkFont = opt.font;
54 linkFont.setPointSize(linkFont.pointSize() - 1);
56 const QFontMetrics linkMetrics(linkFont);
58 int leftPosition = m_padding * 2;
59 int rightPosition = opt.rect.right() - m_padding;
61 opt.state |= QStyle::State_Active;
63 const QIcon::Mode iconMode = opt.state & QStyle::State_Selected ? QIcon::Selected : QIcon::Normal;
65 const QPalette::ColorRole colorRole = opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text;
66 const QPalette::ColorRole colorLinkRole = opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Link;
69 opt.palette.setColor(QPalette::All, QPalette::HighlightedText, opt.palette.color(QPalette::Active, QPalette::Text));
70 opt.palette.setColor(QPalette::All, QPalette::Highlight, opt.palette.base().color().darker(108));
73 QPalette textPalette = opt.palette;
74 textPalette.setCurrentColorGroup(opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled);
77 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, w);
83 bool isWebSearch = isSearchSuggestion;
87 if (isVisitSearchItem) {
90 if (!m_forceVisitItem) {
96 const int iconSize = 16;
97 const int iconYPos = center - (iconSize / 2);
98 QRect iconRect(leftPosition, iconYPos, iconSize, iconSize);
99 QPixmap pixmap = index.data(Qt::DecorationRole).value<QIcon>().pixmap(iconSize);
100 if (isSearchSuggestion || (isVisitSearchItem && isWebSearch)) {
101 pixmap = QIcon::fromTheme(
QSL(
"edit-find"), QIcon(
QSL(
":icons/menu/search-icon.svg"))).pixmap(iconSize, iconMode);
103 if (isVisitSearchItem && bookmark) {
104 pixmap = bookmark->
icon().pixmap(iconSize);
110 painter->drawPixmap(iconRect, pixmap);
111 leftPosition = iconRect.right() + m_padding * 2;
114 int starPixmapWidth = 0;
117 const QSize starSize(16, 16);
118 starPixmapWidth = starSize.width();
119 QPoint pos(rightPosition - starPixmapWidth, center - starSize.height() / 2);
120 QRect starRect(pos, starSize);
121 painter->drawPixmap(starRect, icon.pixmap(starSize, iconMode));
128 QRect titleRect(leftPosition, center - opt.fontMetrics.height() / 2, opt.rect.width() * 0.6, opt.fontMetrics.height());
130 painter->setFont(opt.font);
132 if (isVisitSearchItem) {
134 title = bookmark->
title();
141 leftPosition += viewItemDrawText(painter, &opt, titleRect, title, textPalette.color(colorRole), searchText);
142 leftPosition += m_padding * 2;
145 const int maxChars = (opt.rect.width() - leftPosition) / opt.fontMetrics.horizontalAdvance(
QL1C(
'i'));
147 const QByteArray linkArray = index.data(Qt::DisplayRole).toByteArray();
148 if (!linkArray.startsWith(
"data") && !linkArray.startsWith(
"javascript")) {
149 link = QString::fromUtf8(QByteArray::fromPercentEncoding(linkArray)).left(maxChars);
151 link = QString::fromLatin1(linkArray.left(maxChars));
154 if (isVisitSearchItem || isSearchSuggestion) {
155 if (!opt.state.testFlag(QStyle::State_Selected) && !opt.state.testFlag(QStyle::State_MouseOver)) {
157 }
else if (isVisitSearchItem && (!isWebSearch || m_forceVisitItem)) {
161 if (searchEngineName.isEmpty()) {
164 link = tr(
"Search with %1").arg(searchEngineName);
169 link = bookmark->
url().toString();
173 if (!link.isEmpty()) {
174 QChar separator =
QL1C(
'-');
175 QRect separatorRect(leftPosition, center - linkMetrics.height() / 2, linkMetrics.horizontalAdvance(separator), linkMetrics.height());
176 style->drawItemText(painter, separatorRect, Qt::AlignCenter, textPalette,
true, separator, colorRole);
177 leftPosition += separatorRect.width() + m_padding * 2;
181 const int leftLinkEdge = leftPosition;
182 const int rightLinkEdge = rightPosition - m_padding - starPixmapWidth;
183 QRect linkRect(leftLinkEdge, center - linkMetrics.height() / 2, rightLinkEdge - leftLinkEdge, linkMetrics.height());
184 painter->setFont(linkFont);
189 if (
qzSettings->showSwitchTab && !m_forceVisitItem && tabPos != -1) {
190 const QIcon tabIcon = QIcon(
QSL(
":icons/menu/tab.svg"));
191 QRect iconRect(linkRect);
192 iconRect.setX(iconRect.x());
193 iconRect.setWidth(16);
194 painter->drawPixmap(iconRect, tabIcon.pixmap(iconRect.size(), iconMode));
196 QRect textRect(linkRect);
197 textRect.setX(textRect.x() + m_padding + 16 + m_padding);
198 viewItemDrawText(painter, &opt, textRect, tr(
"Switch to tab"), textPalette.color(colorLinkRole));
199 }
else if (isVisitSearchItem || isSearchSuggestion) {
200 viewItemDrawText(painter, &opt, linkRect, link, textPalette.color(colorLinkRole));
202 viewItemDrawText(painter, &opt, linkRect, link, textPalette.color(colorLinkRole), searchText);
206 if (!(opt.state & QStyle::State_Selected)) {
207 QRect lineRect(opt.rect.left(), opt.rect.bottom(), opt.rect.width(), 1);
208 painter->fillRect(lineRect, opt.palette.color(QPalette::AlternateBase));
217 QStyleOptionViewItem opt(option);
218 initStyleOption(&opt, index);
220 const QWidget* w = opt.widget;
221 const QStyle* style = w ? w->style() : QApplication::style();
222 const int padding = style->pixelMetric(QStyle::PM_FocusFrameHMargin,
nullptr) + 1;
224 m_padding = padding > 3 ? padding : 3;
225 m_rowHeight = 4 * m_padding + qMax(16, opt.fontMetrics.height());
228 return QSize(200, m_rowHeight);
233 m_forceVisitItem = enable;
236static bool sizeBiggerThan(
const QString &s1,
const QString &s2)
238 return s1.size() > s2.size();
241static QSizeF viewItemTextLayout(QTextLayout &textLayout,
int lineWidth)
245 textLayout.beginLayout();
246 QTextLine line = textLayout.createLine();
247 if (line.isValid()) {
248 line.setLineWidth(lineWidth);
249 line.setPosition(QPointF(0, height));
250 height += line.height();
251 widthUsed = qMax(widthUsed, line.naturalTextWidth());
253 textLayout.endLayout();
255 return QSizeF(widthUsed, height);
260int LocationCompleterDelegate::viewItemDrawText(QPainter *p,
const QStyleOptionViewItem *option,
const QRect &rect,
261 const QString &text,
const QColor &color,
const QString &searchText)
const
263 if (text.isEmpty()) {
267 const QFontMetrics fontMetrics(p->font());
268 QString elidedText = fontMetrics.elidedText(text, option->textElideMode, rect.width());
269 QTextOption textOption;
270 textOption.setWrapMode(QTextOption::NoWrap);
271 textOption.setAlignment(QStyle::visualAlignment(textOption.textDirection(), option->displayAlignment));
272 QTextLayout textLayout;
273 textLayout.setFont(p->font());
274 textLayout.setText(elidedText);
275 textLayout.setTextOption(textOption);
277 if (!searchText.isEmpty()) {
278 QList<int> delimiters;
279 QStringList searchStrings = searchText.split(QLatin1Char(
' '), Qt::SkipEmptyParts);
281 std::sort(searchStrings.begin(), searchStrings.end(), sizeBiggerThan);
283 for (
const QString &
string : std::as_const(searchStrings)) {
284 int delimiter = text.indexOf(
string, 0, Qt::CaseInsensitive);
286 while (delimiter != -1) {
287 int start = delimiter;
288 int end = delimiter +
string.length();
289 bool alreadyContains =
false;
290 for (
int i = 0;
i < delimiters.count(); ++
i) {
291 int dStart = delimiters.at(
i);
292 int dEnd = delimiters.at(++
i);
294 if (dStart <= start && dEnd >= end) {
295 alreadyContains =
true;
299 if (!alreadyContains) {
300 delimiters.append(start);
301 delimiters.append(end);
304 delimiter = text.indexOf(
string, end, Qt::CaseInsensitive);
309 std::sort(delimiters.begin(), delimiters.end());
312 if (!delimiters.isEmpty() && !(delimiters.count() % 2)) {
313 QList<QTextLayout::FormatRange> highlightParts;
315 while (!delimiters.isEmpty()) {
316 QTextLayout::FormatRange highlightedPart;
317 int start = delimiters.takeFirst();
318 int end = delimiters.takeFirst();
319 highlightedPart.start = start;
320 highlightedPart.length = end - start;
321 highlightedPart.format.setFontWeight(QFont::Bold);
322 highlightedPart.format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
324 highlightParts << highlightedPart;
327 textLayout.setFormats(highlightParts);
332 viewItemTextLayout(textLayout, rect.width());
334 if (textLayout.lineCount() <= 0) {
338 QTextLine textLine = textLayout.lineAt(0);
342 int diff = textLine.naturalTextWidth() - rect.width();
344 elidedText = fontMetrics.elidedText(elidedText, option->textElideMode, rect.width() - diff);
346 textLayout.setText(elidedText);
348 viewItemTextLayout(textLayout, rect.width());
350 if (textLayout.lineCount() <= 0) {
353 textLine = textLayout.lineAt(0);
358 qreal width = qMax<qreal>(rect.width(), textLayout.lineAt(0).width());
359 const QRect &layoutRect = QStyle::alignedRect(option->direction, option->displayAlignment, QSize(
int(width),
int(textLine.height())), rect);
360 const QPointF &position = layoutRect.topLeft();
362 textLine.draw(p, position);
364 return qMin<int>(rect.width(), textLayout.lineAt(0).naturalTextWidth());
static IconProvider * instance()
static SearchEngine searchEngine()
static LoadAction loadAction(const QString &text)
void setForceVisitItem(bool enable)
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
LocationCompleterDelegate(QObject *parent=nullptr)
SearchEngine searchEngine