| 1 | /* |
| 2 | SPDX-FileCopyrightText: 2007 David Nolden <david.nolden.kdevelop@art-master.de> |
| 3 | SPDX-FileCopyrightText: 2022 Waqar Ahmed <waqar.17a@gmail.com> |
| 4 | |
| 5 | SPDX-License-Identifier: LGPL-2.0-or-later |
| 6 | */ |
| 7 | |
| 8 | #include "katecompletiondelegate.h" |
| 9 | |
| 10 | #include <ktexteditor/codecompletionmodel.h> |
| 11 | |
| 12 | #include "katecompletionmodel.h" |
| 13 | #include "katepartdebug.h" |
| 14 | |
| 15 | #include <QApplication> |
| 16 | #include <QPainter> |
| 17 | |
| 18 | KateCompletionDelegate::KateCompletionDelegate(QObject *parent) |
| 19 | : QStyledItemDelegate(parent) |
| 20 | { |
| 21 | } |
| 22 | |
| 23 | static void paintItemViewText(QPainter *p, const QString &text, const QStyleOptionViewItem &options, QList<QTextLayout::FormatRange> formats) |
| 24 | { |
| 25 | // set formats |
| 26 | QTextLayout textLayout(text, options.font, p->device()); |
| 27 | auto fmts = textLayout.formats(); |
| 28 | formats.append(l: fmts); |
| 29 | textLayout.setFormats(formats); |
| 30 | |
| 31 | // set alignment, rtls etc |
| 32 | QTextOption textOption; |
| 33 | textOption.setTextDirection(options.direction); |
| 34 | textOption.setAlignment(QStyle::visualAlignment(direction: options.direction, alignment: options.displayAlignment)); |
| 35 | textLayout.setTextOption(textOption); |
| 36 | |
| 37 | // layout the text |
| 38 | textLayout.beginLayout(); |
| 39 | |
| 40 | QTextLine line = textLayout.createLine(); |
| 41 | if (!line.isValid()) |
| 42 | return; |
| 43 | |
| 44 | const int lineWidth = options.rect.width(); |
| 45 | line.setLineWidth(lineWidth); |
| 46 | line.setPosition(QPointF(0, 0)); |
| 47 | |
| 48 | textLayout.endLayout(); |
| 49 | |
| 50 | int y = QStyle::alignedRect(direction: Qt::LayoutDirectionAuto, alignment: options.displayAlignment, size: textLayout.boundingRect().size().toSize(), rectangle: options.rect).y(); |
| 51 | |
| 52 | // draw the text |
| 53 | const auto pos = QPointF(options.rect.x(), y); |
| 54 | textLayout.draw(p, pos); |
| 55 | } |
| 56 | |
| 57 | void KateCompletionDelegate::paint(QPainter *painter, const QStyleOptionViewItem &o, const QModelIndex &index) const |
| 58 | { |
| 59 | QStyleOptionViewItem opt = o; |
| 60 | initStyleOption(option: &opt, index); |
| 61 | QString text = opt.text; |
| 62 | |
| 63 | if (text.isEmpty()) { |
| 64 | QStyledItemDelegate::paint(painter, option: o, index); |
| 65 | return; |
| 66 | } |
| 67 | |
| 68 | auto *style = opt.widget->style() ? opt.widget->style() : qApp->style(); |
| 69 | |
| 70 | opt.text.clear(); |
| 71 | |
| 72 | style->drawControl(element: QStyle::CE_ItemViewItem, opt: &opt, p: painter, w: opt.widget); |
| 73 | |
| 74 | QRect textRect = style->subElementRect(subElement: QStyle::SE_ItemViewItemText, option: &opt, widget: opt.widget); |
| 75 | |
| 76 | const bool isGroup = index.data(arole: KateCompletionModel::IsNonEmptyGroup).toBool(); |
| 77 | if (!isGroup && !opt.features.testFlag(flag: QStyleOptionViewItem::HasDecoration)) { |
| 78 | // 3 because 2 margins for the icon, and one left margin for the text |
| 79 | int hMargins = style->pixelMetric(metric: QStyle::PM_FocusFrameHMargin) * 3; |
| 80 | textRect.adjust(dx1: hMargins + opt.decorationSize.width(), dy1: 0, dx2: 0, dy2: 0); |
| 81 | } |
| 82 | |
| 83 | #if 0 |
| 84 | auto p = painter->pen(); |
| 85 | painter->setPen(Qt::yellow); |
| 86 | painter->drawRect(opt.rect); |
| 87 | |
| 88 | painter->setPen(Qt::red); |
| 89 | painter->drawRect(textRect); |
| 90 | painter->setPen(p); |
| 91 | #endif |
| 92 | |
| 93 | auto highlightings = createHighlighting(index); |
| 94 | opt.rect = textRect; |
| 95 | opt.displayAlignment = m_alignTop ? Qt::AlignTop : Qt::AlignVCenter; |
| 96 | paintItemViewText(p: painter, text, options: opt, formats: std::move(highlightings)); |
| 97 | } |
| 98 | |
| 99 | QSize KateCompletionDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const |
| 100 | { |
| 101 | if (index.data().toString().isEmpty()) { |
| 102 | return QStyledItemDelegate::sizeHint(option, index); |
| 103 | } |
| 104 | |
| 105 | QSize size = QStyledItemDelegate::sizeHint(option, index); |
| 106 | if (!index.data(arole: Qt::DecorationRole).isNull()) { |
| 107 | return size; |
| 108 | } |
| 109 | const int hMargins = option.widget->style()->pixelMetric(metric: QStyle::PM_FocusFrameHMargin) * 3; |
| 110 | size.rwidth() += option.decorationSize.width() + hMargins; |
| 111 | return size; |
| 112 | } |
| 113 | |
| 114 | static QList<QTextLayout::FormatRange> highlightingFromVariantList(const QList<QVariant> &customHighlights) |
| 115 | { |
| 116 | QList<QTextLayout::FormatRange> ret; |
| 117 | |
| 118 | for (int i = 0; i + 2 < customHighlights.count(); i += 3) { |
| 119 | if (!customHighlights[i].canConvert<int>() || !customHighlights[i + 1].canConvert<int>() || !customHighlights[i + 2].canConvert<QTextFormat>()) { |
| 120 | qCWarning(LOG_KTE) << "Unable to convert triple to custom formatting." ; |
| 121 | continue; |
| 122 | } |
| 123 | |
| 124 | QTextLayout::FormatRange format; |
| 125 | format.start = customHighlights[i].toInt(); |
| 126 | format.length = customHighlights[i + 1].toInt(); |
| 127 | format.format = customHighlights[i + 2].value<QTextFormat>().toCharFormat(); |
| 128 | |
| 129 | if (!format.format.isValid()) { |
| 130 | qCWarning(LOG_KTE) << "Format is not valid" ; |
| 131 | } |
| 132 | |
| 133 | ret << format; |
| 134 | } |
| 135 | return ret; |
| 136 | } |
| 137 | |
| 138 | QList<QTextLayout::FormatRange> KateCompletionDelegate::createHighlighting(const QModelIndex &index) |
| 139 | { |
| 140 | QVariant highlight = index.data(arole: KTextEditor::CodeCompletionModel::HighlightingMethod); |
| 141 | |
| 142 | // TODO: config enable specifying no highlight as default |
| 143 | int highlightMethod = KTextEditor::CodeCompletionModel::InternalHighlighting; |
| 144 | if (highlight.canConvert<int>()) { |
| 145 | highlightMethod = highlight.toInt(); |
| 146 | } |
| 147 | |
| 148 | if (highlightMethod & KTextEditor::CodeCompletionModel::CustomHighlighting) { |
| 149 | return highlightingFromVariantList(customHighlights: index.data(arole: KTextEditor::CodeCompletionModel::CustomHighlight).toList()); |
| 150 | } |
| 151 | |
| 152 | return {}; |
| 153 | } |
| 154 | |