mirror of
https://invent.kde.org/network/falkon.git
synced 2024-11-14 02:52:12 +01:00
[LocationCompleter] New highlighter algorithm using QTextLayout that fixes rendering of RTL strings.
This commit is contained in:
parent
7a1f4c77cd
commit
877266b418
|
@ -24,6 +24,7 @@
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QMouseEvent>
|
#include <QMouseEvent>
|
||||||
|
#include <QTextLayout>
|
||||||
|
|
||||||
LocationCompleterDelegate::LocationCompleterDelegate(LocationCompleterView* parent)
|
LocationCompleterDelegate::LocationCompleterDelegate(LocationCompleterView* parent)
|
||||||
: QStyledItemDelegate(parent)
|
: QStyledItemDelegate(parent)
|
||||||
|
@ -100,10 +101,10 @@ void LocationCompleterDelegate::paint(QPainter* painter, const QStyleOptionViewI
|
||||||
// RTL Support: remove conflicting of right-aligned text and starpixmap!
|
// RTL Support: remove conflicting of right-aligned text and starpixmap!
|
||||||
const int rightTitleEdge = rightPosition - m_padding - starPixmapWidth;
|
const int rightTitleEdge = rightPosition - m_padding - starPixmapWidth;
|
||||||
QRect titleRect(leftTitleEdge, opt.rect.top() + m_padding, rightTitleEdge - leftTitleEdge, titleMetrics.height());
|
QRect titleRect(leftTitleEdge, opt.rect.top() + m_padding, rightTitleEdge - leftTitleEdge, titleMetrics.height());
|
||||||
QString title(titleMetrics.elidedText(index.data(LocationCompleterModel::TitleRole).toString(), Qt::ElideRight, titleRect.width()));
|
QString title = index.data(LocationCompleterModel::TitleRole).toString();
|
||||||
painter->setFont(titleFont);
|
painter->setFont(titleFont);
|
||||||
|
|
||||||
drawHighlightedTextLine(titleRect, title, searchText, painter, style, opt, colorRole);
|
viewItemDrawText(painter, &opt, titleRect, title, colorRole, searchText);
|
||||||
|
|
||||||
// Draw link
|
// Draw link
|
||||||
const int infoYPos = titleRect.bottom() + opt.fontMetrics.leading() + 2;
|
const int infoYPos = titleRect.bottom() + opt.fontMetrics.leading() + 2;
|
||||||
|
@ -122,7 +123,6 @@ void LocationCompleterDelegate::paint(QPainter* painter, const QStyleOptionViewI
|
||||||
link = QString::fromLatin1(linkArray.left(500));
|
link = QString::fromLatin1(linkArray.left(500));
|
||||||
}
|
}
|
||||||
|
|
||||||
link = opt.fontMetrics.elidedText(link, Qt::ElideRight, linkRect.width());
|
|
||||||
painter->setFont(opt.font);
|
painter->setFont(opt.font);
|
||||||
|
|
||||||
// Draw url (or switch to tab)
|
// Draw url (or switch to tab)
|
||||||
|
@ -136,10 +136,10 @@ void LocationCompleterDelegate::paint(QPainter* painter, const QStyleOptionViewI
|
||||||
|
|
||||||
QRect textRect(linkRect);
|
QRect textRect(linkRect);
|
||||||
textRect.setX(textRect.x() + m_padding + 16 + m_padding);
|
textRect.setX(textRect.x() + m_padding + 16 + m_padding);
|
||||||
drawTextLine(textRect, LocationCompleterView::tr("Switch to tab"), painter, style, opt, colorLinkRole);
|
viewItemDrawText(painter, &opt, textRect, LocationCompleterView::tr("Switch to tab"), colorLinkRole);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
drawHighlightedTextLine(linkRect, link, searchText, painter, style, opt, colorLinkRole);
|
viewItemDrawText(painter, &opt, linkRect, link, colorLinkRole, searchText);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw line at the very bottom of item if the item is not highlighted
|
// Draw line at the very bottom of item if the item is not highlighted
|
||||||
|
@ -185,29 +185,54 @@ bool LocationCompleterDelegate::drawSwitchToTab() const
|
||||||
return qzSettings->showSwitchTab && m_drawSwitchToTab;
|
return qzSettings->showSwitchTab && m_drawSwitchToTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
QRect LocationCompleterDelegate::adjustRect(const QRect &original, const QRect &created) const
|
|
||||||
{
|
|
||||||
if (created.left() + created.width() >= original.right()) {
|
|
||||||
QRect nRect = created;
|
|
||||||
nRect.setWidth(original.right() - created.left());
|
|
||||||
return nRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
return created;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sizeBiggerThan(const QString &s1, const QString &s2)
|
static bool sizeBiggerThan(const QString &s1, const QString &s2)
|
||||||
{
|
{
|
||||||
return s1.size() > s2.size();
|
return s1.size() > s2.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocationCompleterDelegate::drawHighlightedTextLine(const QRect &rect, const QString &text, const QString &searchText,
|
static QSizeF viewItemTextLayout(QTextLayout &textLayout, int lineWidth)
|
||||||
QPainter* painter, const QStyle* style, const QStyleOptionViewItemV4 &option,
|
|
||||||
const QPalette::ColorRole &role) const
|
|
||||||
{
|
{
|
||||||
|
qreal height = 0;
|
||||||
|
qreal widthUsed = 0;
|
||||||
|
textLayout.beginLayout();
|
||||||
|
QTextLine line = textLayout.createLine();
|
||||||
|
if (line.isValid()) {
|
||||||
|
line.setLineWidth(lineWidth);
|
||||||
|
line.setPosition(QPointF(0, height));
|
||||||
|
height += line.height();
|
||||||
|
widthUsed = qMax(widthUsed, line.naturalTextWidth());
|
||||||
|
|
||||||
|
textLayout.endLayout();
|
||||||
|
}
|
||||||
|
return QSizeF(widthUsed, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// most of codes taken from QCommonStylePrivate::viewItemDrawText()
|
||||||
|
// added highlighting and simplified for single-line textlayouts
|
||||||
|
void LocationCompleterDelegate::viewItemDrawText(QPainter *p, const QStyleOptionViewItemV4 *option, const QRect &rect,
|
||||||
|
const QString &text, const QPalette::ColorRole &role, const QString &searchText) const
|
||||||
|
{
|
||||||
|
const QColor &color = option->palette.color(role);
|
||||||
|
const QWidget *widget = option->widget;
|
||||||
|
const QStyle* proxyStyle = widget ? widget->style()->proxy() : QApplication::style()->proxy();
|
||||||
|
const int textMargin = proxyStyle->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, widget) + 1;
|
||||||
|
|
||||||
|
QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding
|
||||||
|
const QFontMetrics &fontMetrics(p->font());
|
||||||
|
// a workaround for not drawing highlighted text on icon
|
||||||
|
const QString &elidedText = fontMetrics.elidedText(text, option->textElideMode, textRect.width() - 2 * m_padding - 2);
|
||||||
|
QTextOption textOption;
|
||||||
|
textOption.setWrapMode(QTextOption::NoWrap);
|
||||||
|
textOption.setTextDirection(text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight);
|
||||||
|
textOption.setAlignment(QStyle::visualAlignment(textOption.textDirection(), option->displayAlignment));
|
||||||
|
QTextLayout textLayout;
|
||||||
|
textLayout.setFont(p->font());
|
||||||
|
textLayout.setText(elidedText);
|
||||||
|
textLayout.setTextOption(textOption);
|
||||||
|
|
||||||
|
if (!searchText.isEmpty()) {
|
||||||
QList<int> delimiters;
|
QList<int> delimiters;
|
||||||
QStringList searchStrings = searchText.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
QStringList searchStrings = searchText.split(QLatin1Char(' '), QString::SkipEmptyParts);
|
||||||
|
|
||||||
// Look for longer parts first
|
// Look for longer parts first
|
||||||
qSort(searchStrings.begin(), searchStrings.end(), sizeBiggerThan);
|
qSort(searchStrings.begin(), searchStrings.end(), sizeBiggerThan);
|
||||||
|
|
||||||
|
@ -217,7 +242,6 @@ void LocationCompleterDelegate::drawHighlightedTextLine(const QRect &rect, const
|
||||||
while (delimiter != -1) {
|
while (delimiter != -1) {
|
||||||
int start = delimiter;
|
int start = delimiter;
|
||||||
int end = delimiter + string.length();
|
int end = delimiter + string.length();
|
||||||
|
|
||||||
bool alreadyContains = false;
|
bool alreadyContains = false;
|
||||||
for (int i = 0; i < delimiters.count(); ++i) {
|
for (int i = 0; i < delimiters.count(); ++i) {
|
||||||
int dStart = delimiters.at(i);
|
int dStart = delimiters.at(i);
|
||||||
|
@ -228,7 +252,6 @@ void LocationCompleterDelegate::drawHighlightedTextLine(const QRect &rect, const
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!alreadyContains) {
|
if (!alreadyContains) {
|
||||||
delimiters.append(start);
|
delimiters.append(start);
|
||||||
delimiters.append(end);
|
delimiters.append(end);
|
||||||
|
@ -242,93 +265,48 @@ void LocationCompleterDelegate::drawHighlightedTextLine(const QRect &rect, const
|
||||||
qSort(delimiters);
|
qSort(delimiters);
|
||||||
|
|
||||||
// If we don't find any match, just paint it without any highlight
|
// If we don't find any match, just paint it without any highlight
|
||||||
if (delimiters.isEmpty() || delimiters.count() % 2) {
|
if (!delimiters.isEmpty() && !(delimiters.count() % 2)) {
|
||||||
drawTextLine(rect, text, painter, style, option, role);
|
QList<QTextLayout::FormatRange> highlightParts;
|
||||||
return;
|
|
||||||
|
QTextLayout::FormatRange lighterWholeLine;
|
||||||
|
lighterWholeLine.start = 0;
|
||||||
|
lighterWholeLine.length = elidedText.size();
|
||||||
|
QColor lighterColor = color.lighter(130);
|
||||||
|
if (lighterColor == color) {
|
||||||
|
lighterColor = QColor(Qt::gray).darker(180);
|
||||||
}
|
}
|
||||||
|
lighterWholeLine.format.setForeground(lighterColor);
|
||||||
QFont normalFont = painter->font();
|
highlightParts << lighterWholeLine;
|
||||||
QFont boldFont = normalFont;
|
|
||||||
boldFont.setBold(true);
|
|
||||||
|
|
||||||
QFontMetrics normalMetrics(normalFont);
|
|
||||||
QFontMetrics boldMetrics(boldFont);
|
|
||||||
|
|
||||||
int lastEndPos = 0;
|
|
||||||
int lastRectPos = rect.left();
|
|
||||||
|
|
||||||
while (!delimiters.isEmpty()) {
|
while (!delimiters.isEmpty()) {
|
||||||
|
QTextLayout::FormatRange highlightedPart;
|
||||||
int start = delimiters.takeFirst();
|
int start = delimiters.takeFirst();
|
||||||
int end = delimiters.takeFirst();
|
int end = delimiters.takeFirst();
|
||||||
|
highlightedPart.start = start;
|
||||||
|
highlightedPart.length = end - start;
|
||||||
|
highlightedPart.format.setFontWeight(QFont::Bold);
|
||||||
|
highlightedPart.format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
|
||||||
|
highlightedPart.format.setForeground(color);
|
||||||
|
|
||||||
const QString normalPart = text.mid(lastEndPos, start - lastEndPos);
|
highlightParts << highlightedPart;
|
||||||
const QString boldPart = text.mid(start, end - start);
|
|
||||||
|
|
||||||
lastEndPos = end;
|
|
||||||
|
|
||||||
if (!normalPart.isEmpty()) {
|
|
||||||
int width = normalMetrics.width(normalPart);
|
|
||||||
QRect nRect = adjustRect(rect, QRect(lastRectPos, rect.top(), width, rect.height()));
|
|
||||||
|
|
||||||
if (nRect.width() > 0) {
|
|
||||||
if (text.isRightToLeft()) {
|
|
||||||
nRect = style->visualRect(Qt::RightToLeft, rect, nRect);
|
|
||||||
}
|
}
|
||||||
painter->setFont(normalFont);
|
|
||||||
drawTextLine(nRect, normalPart, painter, style, option, role);
|
|
||||||
|
|
||||||
lastRectPos += nRect.width();
|
textLayout.setAdditionalFormats(highlightParts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!boldPart.isEmpty()) {
|
// do layout
|
||||||
int width = boldMetrics.width(boldPart);
|
viewItemTextLayout(textLayout, textRect.width());
|
||||||
QRect bRect = adjustRect(rect, QRect(lastRectPos, rect.top(), width, rect.height()));
|
|
||||||
|
|
||||||
if (bRect.width() > 0) {
|
// draw line
|
||||||
if (text.isRightToLeft()) {
|
p->setPen(color);
|
||||||
bRect = style->visualRect(Qt::RightToLeft, rect, bRect);
|
const int lineCount = textLayout.lineCount();
|
||||||
}
|
if (lineCount > 0) {
|
||||||
painter->setFont(boldFont);
|
qreal height = textLayout.lineAt(0).height();
|
||||||
drawTextLine(bRect, boldPart, painter, style, option, role);
|
qreal width = qMax<qreal>(textRect.width(), textLayout.lineAt(0).width());
|
||||||
|
const QRect &layoutRect = QStyle::alignedRect(option->direction, option->displayAlignment, QSize(int(width), int(height)), textRect);
|
||||||
|
const QPointF &position = layoutRect.topLeft();
|
||||||
|
|
||||||
// Paint manually line under text instead of using QFont::underline
|
textLayout.lineAt(0).draw(p, position);
|
||||||
QRect underlineRect(bRect.left(), bRect.top() + boldMetrics.ascent() + 1,
|
|
||||||
bRect.width(), boldFont.pointSize() > 8 ? 2 : 1);
|
|
||||||
|
|
||||||
painter->fillRect(underlineRect, option.palette.color(role));
|
|
||||||
|
|
||||||
lastRectPos += bRect.width();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delimiters.isEmpty() && lastEndPos != text.size()) {
|
|
||||||
const QString lastText = text.mid(lastEndPos);
|
|
||||||
|
|
||||||
int width = normalMetrics.width(lastText);
|
|
||||||
QRect nRect = adjustRect(rect, QRect(lastRectPos, rect.top(), width, rect.height()));
|
|
||||||
if (text.isRightToLeft()) {
|
|
||||||
nRect = style->visualRect(Qt::RightToLeft, rect, nRect);
|
|
||||||
}
|
|
||||||
painter->setFont(normalFont);
|
|
||||||
drawTextLine(nRect, lastText, painter, style, option, role);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LocationCompleterDelegate::drawTextLine(const QRect &rect, QString text, QPainter* painter,
|
|
||||||
const QStyle* style, const QStyleOptionViewItemV4 &option,
|
|
||||||
const QPalette::ColorRole &role) const
|
|
||||||
{
|
|
||||||
if (rect.width() > 0) {
|
|
||||||
const Qt::LayoutDirection direction = option.widget ? option.widget->layoutDirection() : QApplication::layoutDirection();
|
|
||||||
Qt::LayoutDirection textDirection = text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight;
|
|
||||||
Qt::Alignment alignment = textDirection == direction ? Qt::AlignLeft : Qt::AlignRight;
|
|
||||||
|
|
||||||
// Insert unicode control characters: prepend RLE or LRE and append (RLM or LRM)+PDF
|
|
||||||
text.isRightToLeft() ? text.prepend(QChar(0x202B)).append(0x200F) : text.prepend(QChar(0x202A)).append(0x200E);
|
|
||||||
text.append(QChar(0x202C));
|
|
||||||
|
|
||||||
style->drawItemText(painter, rect, Qt::TextSingleLine | alignment, option.palette, true, text, role);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,15 +36,10 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool drawSwitchToTab() const;
|
bool drawSwitchToTab() const;
|
||||||
QRect adjustRect(const QRect &original, const QRect &created) const;
|
|
||||||
|
|
||||||
void drawHighlightedTextLine(const QRect &rect, const QString &text, const QString &searchText,
|
void viewItemDrawText(QPainter *p, const QStyleOptionViewItemV4 *option, const QRect &rect,
|
||||||
QPainter* painter, const QStyle* style, const QStyleOptionViewItemV4 &option,
|
const QString &text, const QPalette::ColorRole &role,
|
||||||
const QPalette::ColorRole &role) const;
|
const QString &searchText = QString()) const;
|
||||||
|
|
||||||
void drawTextLine(const QRect &rect, QString text, QPainter* painter,
|
|
||||||
const QStyle* style, const QStyleOptionViewItemV4 &option,
|
|
||||||
const QPalette::ColorRole &role) const;
|
|
||||||
|
|
||||||
mutable int m_rowHeight;
|
mutable int m_rowHeight;
|
||||||
mutable int m_padding;
|
mutable int m_padding;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user