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(" &nbsp;")
-                    + 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