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
18KateCompletionDelegate::KateCompletionDelegate(QObject *parent)
19 : QStyledItemDelegate(parent)
20{
21}
22
23static 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
57void 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
99QSize 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
114static 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
138QList<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

source code of ktexteditor/src/completion/katecompletiondelegate.cpp