From 05c276958d0b970126167310cc7bb79a8c5fa8de Mon Sep 17 00:00:00 2001 From: Waqar Ahmed <waqar.17a@gmail.com> Date: Thu, 18 Feb 2021 00:12:17 +0500 Subject: [PATCH 1/2] Refactor painting and textLayout creation outside of command bar --- kate/katecommandbar.cpp | 58 +++++++------------------------------ shared/kfts_fuzzy_match.h | 61 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 52 deletions(-) diff --git a/kate/katecommandbar.cpp b/kate/katecommandbar.cpp index ac8466c108..356e98d227 100644 --- a/kate/katecommandbar.cpp +++ b/kate/katecommandbar.cpp @@ -67,20 +67,6 @@ private: QString m_pattern; }; -static void layoutViewItemText(QTextLayout &textLayout, int lineWidth) -{ - textLayout.beginLayout(); - - QTextLine line = textLayout.createLine(); - if (!line.isValid()) - return; - line.setLineWidth(lineWidth); - line.setPosition(QPointF(0, 0)); - - textLayout.endLayout(); - return; -} - class CommandBarStyleDelegate : public QStyledItemDelegate { public: @@ -114,11 +100,6 @@ public: painter->translate(20, 0); } - QTextOption textOption; - textOption.setTextDirection(options.direction); - textOption.setAlignment(QStyle::visualAlignment(options.direction, options.displayAlignment)); - - uint8_t matches[256]; // must use QString here otherwise fuzzy matching wont // work very well QString str = original; @@ -130,40 +111,21 @@ public: str = str.mid(actionNameStart); } - const int total = kfts::get_fuzzy_match_positions(m_filterString, str, matches); - - using FormatRange = QTextLayout::FormatRange; - QTextCharFormat fmt; - fmt.setFontWeight(QFont::Bold); - fmt.setForeground(options.palette.link()); - QVector<FormatRange> formats; - QTextCharFormat gray; - gray.setForeground(Qt::gray); + QVector<QTextLayout::FormatRange> formats; if (componentIdx > 0) { + QTextCharFormat gray; + gray.setForeground(Qt::gray); formats.append({0, componentIdx, gray}); } - // QTextLayout fails if there are consecutive ranges - // of length = 1 so we have to improvise a little bit - int j = 0; - for (int i = 0; i < total; ++i) { - auto matchPos = actionNameStart + matches[i]; - if (matchPos == j + 1) { - formats.last().length++; - } else { - formats.append({matchPos, 1, fmt}); - } - j = matchPos; - } + QTextCharFormat fmt; + fmt.setForeground(options.palette.link().color()); + fmt.setFontWeight(QFont::Bold); + + const auto f = kfts::get_fuzzy_match_formats(m_filterString, str, componentIdx + 2, fmt); + formats.append(f); - QTextLayout textLayout(original, options.font); - auto fmts = textLayout.formats(); - formats.append(fmts); - textLayout.setFormats(formats); - textLayout.setTextOption(textOption); - layoutViewItemText(textLayout, options.rect.width()); - const auto pos = QPointF(options.rect.x(), options.rect.y()); - textLayout.draw(painter, pos); + kfts::paintItemViewText(painter, original, options, std::move(formats)); painter->restore(); } diff --git a/shared/kfts_fuzzy_match.h b/shared/kfts_fuzzy_match.h index d2e7a329b1..337250f850 100644 --- a/shared/kfts_fuzzy_match.h +++ b/shared/kfts_fuzzy_match.h @@ -9,6 +9,8 @@ #define KFTS_FUZZY_MATCH_H #include <QString> +#include <QStyleOptionViewItem> +#include <QTextLayout> /** * This is based on https://github.com/forrestthewoods/lib_fts/blob/master/code/fts_fuzzy_match.h @@ -316,10 +318,12 @@ static QString to_scored_fuzzy_matched_display_string(const QStringView pattern, return str; } -Q_DECL_UNUSED static int get_fuzzy_match_positions(const QStringView pattern, const QStringView str, uint8_t *matches) +Q_DECL_UNUSED static QVector<QTextLayout::FormatRange> +get_fuzzy_match_formats(const QStringView pattern, const QStringView str, int offset, const QTextCharFormat &fmt) { - if (!matches) { - return 0; + QVector<QTextLayout::FormatRange> ranges; + if (pattern.isEmpty()) { + return ranges; } int totalMatches = 0; @@ -331,8 +335,57 @@ Q_DECL_UNUSED static int get_fuzzy_match_positions(const QStringView pattern, co const auto patternEnd = pattern.cend(); const auto strEnd = str.cend(); + uint8_t matches[256]; fuzzy_internal::fuzzy_match_recursive(patternIt, strIt, score, strIt, strEnd, patternEnd, nullptr, matches, 0, totalMatches, recursionCount); - return totalMatches; + + // QTextCharFormat fmt; + // fmt.setFontWeight(QFont::Bold); + // fmt.setForeground(c); + + int j = 0; + for (int i = 0; i < totalMatches; ++i) { + auto matchPos = matches[i]; + if (matchPos == j + 1) { + ranges.last().length++; + } else { + ranges.append({matchPos + offset, 1, fmt}); + } + j = matchPos; + } + + return ranges; +} + +Q_DECL_UNUSED static void paintItemViewText(QPainter *p, const QString &text, const QStyleOptionViewItem &options, QVector<QTextLayout::FormatRange> formats) +{ + // set formats + QTextLayout textLayout(text, options.font); + auto fmts = textLayout.formats(); + formats.append(fmts); + textLayout.setFormats(formats); + + // set alignment, rtls etc + QTextOption textOption; + textOption.setTextDirection(options.direction); + textOption.setAlignment(QStyle::visualAlignment(options.direction, options.displayAlignment)); + textLayout.setTextOption(textOption); + + // layout the text + textLayout.beginLayout(); + + QTextLine line = textLayout.createLine(); + if (!line.isValid()) + return; + + const int lineWidth = options.rect.width(); + line.setLineWidth(lineWidth); + line.setPosition(QPointF(0, 0)); + + textLayout.endLayout(); + + // draw the text + const auto pos = QPointF(options.rect.x(), options.rect.y()); + textLayout.draw(p, pos); } } // namespace kfts -- GitLab From b6667059e38819eb916d7497642ef9e72978c851 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed <waqar.17a@gmail.com> Date: Thu, 18 Feb 2021 00:13:31 +0500 Subject: [PATCH 2/2] Migrate quickopen style delegate to use QTextLayout --- kate/quickopen/katequickopen.cpp | 48 ++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/kate/quickopen/katequickopen.cpp b/kate/quickopen/katequickopen.cpp index d346fdb9a8..cbc20879ce 100644 --- a/kate/quickopen/katequickopen.cpp +++ b/kate/quickopen/katequickopen.cpp @@ -122,8 +122,6 @@ public: QStyleOptionViewItem options = option; initStyleOption(&options, index); - QTextDocument doc; - QString name = index.data(KateQuickOpenModel::FileName).toString(); QString path = index.data(KateQuickOpenModel::FilePath).toString(); @@ -131,26 +129,35 @@ public: const QString nameColor = option.palette.color(QPalette::Link).name(); - // check if there's a / separtion in filter string - // if there is, we use the last part to highlight the - // filename + QTextCharFormat fmt; + fmt.setForeground(options.palette.link().color()); + fmt.setFontWeight(QFont::Bold); + + const int nameLen = name.length(); + // space between name and path + constexpr int space = 1; + QVector<QTextLayout::FormatRange> formats; + + // collect formats int pos = m_filterString.lastIndexOf(QLatin1Char('/')); if (pos > -1) { ++pos; auto pattern = m_filterString.midRef(pos); - kfts::to_scored_fuzzy_matched_display_string(pattern, name, QStringLiteral("<b style=\"color:%1;\">").arg(nameColor), QStringLiteral("</b>")); + auto nameFormats = kfts::get_fuzzy_match_formats(pattern, name, 0, fmt); + formats.append(nameFormats); } else { - kfts::to_scored_fuzzy_matched_display_string(m_filterString, - name, - QStringLiteral("<b style=\"color:%1;\">").arg(nameColor), - QStringLiteral("</b>")); + auto nameFormats = kfts::get_fuzzy_match_formats(m_filterString, name, 0, fmt); + formats.append(nameFormats); } - kfts::to_scored_fuzzy_matched_display_string(m_filterString, path, QStringLiteral("<b>"), QStringLiteral("</b>")); - - const auto pathFontsize = option.font.pointSize(); - doc.setHtml(QStringLiteral("<span style=\"font-size: %1pt;\">").arg(pathFontsize) + name + QStringLiteral("</span>") + QStringLiteral(" ") - + QStringLiteral("<span style=\"color:gray; font-size:%1pt;\">").arg(pathFontsize - 1) + path + QStringLiteral("</span>")); - doc.setDocumentMargin(2); + QTextCharFormat boldFmt; + boldFmt.setFontWeight(QFont::Bold); + boldFmt.setFontPointSize(options.font.pointSize() - 1); + auto pathFormats = kfts::get_fuzzy_match_formats(m_filterString, name, nameLen + space, boldFmt); + QTextCharFormat gray; + gray.setForeground(Qt::gray); + gray.setFontPointSize(options.font.pointSize() - 1); + formats.append({nameLen + space, path.length(), gray}); + formats.append(pathFormats); painter->save(); @@ -164,12 +171,11 @@ public: options.text = QString(); // clear old text options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter, options.widget); + // space for icon + painter->translate(25, 0); + // draw text - painter->translate(option.rect.x(), option.rect.y()); - if (index.column() == 0) { - painter->translate(25, 0); - } - doc.drawContents(painter); + kfts::paintItemViewText(painter, QString(name + QStringLiteral(" ") + path), options, formats); painter->restore(); } -- GitLab