Commit fd7b2247 authored by Waqar Ahmed's avatar Waqar Ahmed Committed by Christoph Cullmann
Browse files

Improve search results painting

This patch improves the search result drawing in following ways:
- Makes it even closer in looks to the editor
- Fixes the search results for RTL

As a benefit, some of the display related code can be removed from the
model (haven't done so yet for simplicity)
parent a171f796
......@@ -121,6 +121,8 @@ void MatchModel::clear()
m_matchFileIndexHash.clear();
m_matchUnsavedFileIndexHash.clear();
m_lastMatchUrl.clear();
m_maxColNumFound = 0;
m_maxLineNumFound = 0;
endResetModel();
}
......@@ -179,6 +181,10 @@ void MatchModel::addMatches(const QUrl &fileUrl, const QVector<KateSearchMatch>
beginInsertRows(createIndex(fileIndex, 0, FileItemId), matchIndex, matchIndex + searchMatches.size() - 1);
m_matchFiles[fileIndex].matches += searchMatches;
endInsertRows();
const auto &lastMatch = searchMatches.last();
m_maxLineNumFound = std::max(lastMatch.range.start().line(), m_maxLineNumFound);
m_maxColNumFound = std::max(lastMatch.range.start().column(), m_maxColNumFound);
}
void MatchModel::setMatchColors(const QString &foreground, const QString &background, const QString &replaceBackground)
......@@ -594,10 +600,7 @@ QString MatchModel::matchToHtmlString(const Match &match) const
post = post.toHtmlEscaped();
// (line:col)[space][space] ...Line text pre [highlighted match] Line text post....
QString displayText = QStringLiteral("<span style=\"color:%1;\">&nbsp;<b>%2:%3</b></span>&nbsp;")
.arg(m_foregroundColor)
.arg(nbsFormated(match.range.start().line() + 1, 3))
.arg(nbsFormated(match.range.start().column() + 1, 3))
QString displayText = QStringLiteral("%1:%2").arg(nbsFormated(match.range.start().line() + 1, 3)).arg(nbsFormated(match.range.start().column() + 1, 3))
+ pre + matchStr + post;
return displayText;
......@@ -933,6 +936,8 @@ QVariant MatchModel::data(const QModelIndex &index, int role) const
return match.replaceText;
case PlainTextRole:
return matchToPlainText(match);
case MatchItem:
return QVariant::fromValue(match);
}
} else {
qDebug() << "bad index";
......
......@@ -58,7 +58,8 @@ public:
PostMatchRole,
ReplacedRole,
ReplaceTextRole,
PlainTextRole
PlainTextRole,
MatchItem
};
Q_ENUM(MatchDataRoles)
......@@ -227,6 +228,10 @@ private:
QString m_replaceText;
bool m_cancelReplace = true;
// display related for current search
int m_maxLineNumFound = 0;
int m_maxColNumFound = 0;
friend class Results;
};
......
......@@ -18,9 +18,9 @@ public:
endResetModel();
}
private:
bool isMatchItem(const QModelIndex &index) const;
private:
bool parentAcceptsRow(const QModelIndex &source_parent) const;
QString m_text;
......
......@@ -134,3 +134,10 @@ bool Results::replaceSingleMatch(KTextEditor::Document *doc, const QModelIndex &
return matchModel.replaceSingleMatch(doc, sourceIndex, regExp, replaceString);
}
void Results::updateMaxLineNumTouched()
{
auto *delegate = static_cast<SPHtmlDelegate *>(treeView->itemDelegate());
int maxLineFound = matchModel.m_maxLineNumFound;
int maxColFound = matchModel.m_maxColNumFound;
delegate->setMaxLineCol(maxLineFound, maxColFound);
}
......@@ -37,6 +37,9 @@ public:
KTextEditor::Range matchRange(const QModelIndex &matchIndex) const;
bool replaceSingleMatch(KTextEditor::Document *doc, const QModelIndex &matchIndex, const QRegularExpression &regExp, const QString &replaceString);
// Tells the delegate about max line num found in any file. Used while painting
void updateMaxLineNumTouched();
Q_SIGNALS:
void colorsChanged();
};
......
......@@ -5,14 +5,20 @@
*/
#include "htmldelegate.h"
#include "MatchModel.h"
#include "MatchProxyModel.h"
#include <KLocalizedString>
#include <KSyntaxHighlighting/Theme>
#include <KTextEditor/Editor>
#include <QAbstractTextDocumentLayout>
#include <QApplication>
#include <QModelIndex>
#include <QPainter>
#include <QTextCharFormat>
#include <QTextDocument>
#include <kfts_fuzzy_match.h>
#include <ktexteditor_utils.h>
// make list spacing resemble the default list spacing
......@@ -22,14 +28,114 @@ static const int s_ItemMargin = 1;
SPHtmlDelegate::SPHtmlDelegate(QObject *parent)
: QStyledItemDelegate(parent)
{
connect(KTextEditor::Editor::instance(), &KTextEditor::Editor::configChanged, this, [this] {
const auto e = KTextEditor::Editor::instance();
const auto theme = e->theme();
connect(e, &KTextEditor::Editor::configChanged, this, [this] {
m_font = Utils::editorFont();
const auto theme = KTextEditor::Editor::instance()->theme();
m_lineNumColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::LineNumbers));
m_borderColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::Separator));
m_curLineNumColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::CurrentLineNumber));
m_textColor = QColor::fromRgba(theme.textColor(KSyntaxHighlighting::Theme::Normal));
m_iconBorderColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::IconBorder));
m_curLineHighlightColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::CurrentLine));
m_searchColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::SearchHighlight));
m_replaceColor = QColor::fromRgba(theme.editorColor(KSyntaxHighlighting::Theme::ReplaceHighlight));
});
m_font = Utils::editorFont();
}
SPHtmlDelegate::~SPHtmlDelegate()
void SPHtmlDelegate::setMaxLineCol(int line, int col)
{
const QString s = QStringLiteral("%1:%2").arg(line).arg(col);
m_lineNumAreaWidth = QFontMetrics(m_font).horizontalAdvance(s);
}
void SPHtmlDelegate::paintMatchItem(QPainter *p, const QStyleOptionViewItem &opt, const KateSearchMatch &match) const
{
const int line = match.range.start().line() + 1;
const int col = match.range.start().column() + 1;
const QString lineCol = QStringLiteral("%1:%2").arg(line).arg(col);
QStyle *style = opt.widget->style() ? opt.widget->style() : qApp->style();
const auto fm = QFontMetrics(m_font);
static constexpr int hMargins = 2;
const QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &opt, opt.widget);
QRectF iconBorderRect = textRect;
p->save();
p->setFont(m_font);
const bool rtl = opt.direction == Qt::RightToLeft;
// line num area
const bool selected = opt.state & QStyle::State_Selected;
const QBrush iconBorderRectColor = selected ? m_curLineHighlightColor : m_iconBorderColor;
const int lineColWidth = m_lineNumAreaWidth + (hMargins * 2);
if (rtl) {
iconBorderRect.setX(textRect.width() - lineColWidth);
}
iconBorderRect.setWidth(lineColWidth);
// line number area background
p->fillRect(iconBorderRect, iconBorderRectColor);
// line numbers
const QBrush lineNumCol = selected ? m_curLineNumColor : m_lineNumColor;
p->setPen(QPen(lineNumCol, 1));
p->drawText(iconBorderRect.adjusted(2., 0., -2., 0.), lineCol);
// draw the line number area separator line
p->setPen(QPen(m_borderColor, 1));
const QPointF p1 = rtl ? iconBorderRect.topLeft() : iconBorderRect.topRight();
const QPointF p2 = rtl ? iconBorderRect.bottomLeft() : iconBorderRect.bottomRight();
p->drawLine(p1, p2);
// match
p->setPen(QPen(m_textColor, 1));
QString text;
bool replacing = !match.replaceText.isEmpty();
if (replacing) {
text = match.preMatchStr + match.matchStr + match.replaceText + match.postMatchStr;
} else {
text = match.preMatchStr + match.matchStr + match.postMatchStr;
}
QVector<QTextLayout::FormatRange> formats;
QTextLayout::FormatRange fontFmt;
fontFmt.start = 0;
fontFmt.length = text.size();
fontFmt.format.setFont(m_font);
formats << fontFmt;
QTextLayout::FormatRange matchFmt;
matchFmt.start = match.preMatchStr.size();
matchFmt.length = match.matchStr.size();
matchFmt.format.setBackground(m_searchColor);
matchFmt.format.setFontItalic(replacing);
matchFmt.format.setFontStrikeOut(replacing);
formats << matchFmt;
if (replacing) {
QTextLayout::FormatRange repFmt;
repFmt.start = match.preMatchStr.size() + match.matchStr.size();
repFmt.length = match.replaceText.size();
repFmt.format.setBackground(m_replaceColor);
formats << repFmt;
}
// paint the match text
auto opts = opt;
opts.rect = rtl ? textRect.adjusted(0, 0, -(iconBorderRect.width() + hMargins * 2), 0) : textRect.adjusted(iconBorderRect.width() + hMargins * 2, 0, 0, 0);
kfts::paintItemViewText(p, text, opts, formats);
p->restore();
}
void SPHtmlDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
......@@ -37,30 +143,41 @@ void SPHtmlDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option
QStyleOptionViewItem options = option;
initStyleOption(&options, index);
QTextDocument doc;
doc.setDefaultFont(m_font);
doc.setDocumentMargin(s_ItemMargin);
doc.setHtml(index.data().toString());
painter->save();
options.text = QString(); // clear old text
// draw item without text
options.text = QString();
options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget);
// draw area
QRect clip = options.widget->style()->subElementRect(QStyle::SE_ItemViewItemText, &options);
if (index.flags() == Qt::NoItemFlags) {
painter->setBrush(QBrush(QWidget().palette().color(QPalette::Base)));
painter->setPen(QWidget().palette().color(QPalette::Base));
painter->drawRect(QRect(clip.topLeft() - QPoint(20, 0), clip.bottomRight()));
painter->translate(clip.topLeft() - QPoint(20, 0));
Q_ASSERT(index.model() && qstrcmp(index.model()->metaObject()->className(), "MatchProxyModel"));
const auto model = static_cast<const MatchProxyModel *>(index.model());
if (model->isMatchItem(index)) {
const auto item = index.data(MatchModel::MatchItem).value<KateSearchMatch>();
paintMatchItem(painter, options, item);
} else {
painter->translate(clip.topLeft() - QPoint(0, 0));
}
QAbstractTextDocumentLayout::PaintContext pcontext;
pcontext.palette.setColor(QPalette::Text, options.palette.text().color());
doc.documentLayout()->draw(painter, pcontext);
QTextDocument doc;
doc.setDefaultFont(m_font);
doc.setDocumentMargin(s_ItemMargin);
doc.setHtml(index.data().toString());
painter->save();
painter->restore();
// draw area
QRect clip = options.widget->style()->subElementRect(QStyle::SE_ItemViewItemText, &options);
if (index.flags() == Qt::NoItemFlags) {
painter->setBrush(QBrush(QWidget().palette().color(QPalette::Base)));
painter->setPen(QWidget().palette().color(QPalette::Base));
painter->drawRect(QRect(clip.topLeft() - QPoint(20, 0), clip.bottomRight()));
painter->translate(clip.topLeft() - QPoint(20, 0));
} else {
painter->translate(clip.topLeft() - QPoint(0, 0));
}
QAbstractTextDocumentLayout::PaintContext pcontext;
pcontext.palette.setColor(QPalette::Text, options.palette.text().color());
doc.documentLayout()->draw(painter, pcontext);
painter->restore();
}
}
QSize SPHtmlDelegate::sizeHint(const QStyleOptionViewItem & /*option*/, const QModelIndex &index) const
......
......@@ -10,19 +10,33 @@
#include <QFont>
#include <QStyledItemDelegate>
class KateSearchMatch;
class SPHtmlDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit SPHtmlDelegate(QObject *parent);
~SPHtmlDelegate() override;
void paint(QPainter *, const QStyleOptionViewItem &, const QModelIndex &) const override;
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
void setMaxLineCol(int line, int col);
private:
void paintMatchItem(QPainter *, const QStyleOptionViewItem &, const KateSearchMatch &) const;
QFont m_font;
int m_lineNumAreaWidth = 0;
QBrush m_curLineHighlightColor;
QBrush m_iconBorderColor;
QBrush m_borderColor;
QBrush m_lineNumColor;
QBrush m_curLineNumColor;
QBrush m_textColor;
QBrush m_searchColor;
QBrush m_replaceColor;
};
#endif
......@@ -1274,6 +1274,9 @@ void KatePluginSearchView::searchDone()
m_ui.nextButton->setDisabled(m_curResults->matches < 1);
m_ui.filterBtn->setDisabled(m_curResults->matches <= 1);
// let the delegate know about max line num for painting line num area
m_curResults->updateMaxLineNumTouched();
// Set search to done. This sorts the model and collapses all items in the view
m_curResults->matchModel.setSearchState(MatchModel::SearchDone);
......@@ -1319,6 +1322,9 @@ void KatePluginSearchView::searchWhileTypingDone()
m_curResults->treeView->setColumnWidth(0, m_curResults->treeView->width() - 30);
}
// let the delegate know about max line num for painting line num area
m_curResults->updateMaxLineNumTouched();
// Set search to done. This sorts the model and collapses all items in the view
m_curResults->matchModel.setSearchState(MatchModel::SearchDone);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment