/* SPDX-FileCopyrightText: 2024 Waqar Ahmed SPDX-License-Identifier: LGPL-2.0-or-later */ #include "kateargumenthinttree.h" #include "kateargumenthintmodel.h" #include "katecompletionwidget.h" #include #include #include #include #include #include #include class ArgumentHighlighter : public QSyntaxHighlighter { public: ArgumentHighlighter(QTextDocument *doc) : QSyntaxHighlighter(doc) { } void highlightBlock(const QString &) override { for (const auto &f : std::as_const(formats)) { QTextCharFormat fmt = f.format; if (fmt.fontWeight() == QFont::Bold || fmt.fontItalic()) { // bold doesn't work with some fonts for whatever reason // so we just underline as well for fonts where bold won't work fmt.setFontUnderline(true); } setFormat(f.start, f.length, fmt); } } QVector formats; }; static QList highlightingFromVariantList(const QList &customHighlights) { QList ret; for (int i = 0; i + 2 < customHighlights.count(); i += 3) { if (!customHighlights[i].canConvert() || !customHighlights[i + 1].canConvert() || !customHighlights[i + 2].canConvert()) { continue; } QTextLayout::FormatRange format; format.start = customHighlights[i].toInt(); format.length = customHighlights[i + 1].toInt(); format.format = customHighlights[i + 2].value().toCharFormat(); if (!format.format.isValid()) { qWarning() << "Format is not valid"; continue; } ret << format; } return ret; } ArgumentHintWidget::ArgumentHintWidget(KateArgumentHintModel *model, const QFont &font, KateCompletionWidget *completion, QWidget *parent) : QFrame(parent) , m_completionWidget(completion) , m_view(new QPlainTextEdit(this)) , m_currentIndicator(new QLabel(this)) , m_model(model) , m_highlighter(new ArgumentHighlighter(m_view->document())) , m_leftSide(new QWidget(this)) { setAutoFillBackground(true); // we have only 1 top level frame setFrameStyle(QFrame::Box | QFrame::Raised); m_view->setFrameStyle(QFrame::NoFrame); auto upButton = new QToolButton(this); upButton->setAutoRaise(true); upButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up"))); connect(upButton, &QAbstractButton::clicked, this, &ArgumentHintWidget::selectPrevious); auto downButton = new QToolButton(this); downButton->setAutoRaise(true); downButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down"))); connect(downButton, &QAbstractButton::clicked, this, &ArgumentHintWidget::selectNext); auto vLayout = new QVBoxLayout(m_leftSide); vLayout->setContentsMargins({}); vLayout->setAlignment(Qt::AlignCenter); vLayout->addWidget(upButton); vLayout->addWidget(m_currentIndicator); vLayout->addWidget(downButton); auto layout = new QHBoxLayout(this); layout->setContentsMargins({}); layout->setSpacing(0); layout->addWidget(m_leftSide); layout->addWidget(m_view); setFixedWidth(380); m_view->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); m_view->document()->setDefaultFont(font); connect(m_model, &QAbstractItemModel::modelReset, this, [this]() { m_current = -1; selectNext(); }); setVisible(false); } void ArgumentHintWidget::selectNext() { int rowCount = m_model->rowCount(); if (rowCount == 0) { clearAndHide(); return; } m_current = m_current + 1; if (m_current >= rowCount) { m_current = 0; } activateHint(m_current, rowCount); } void ArgumentHintWidget::selectPrevious() { int rowCount = m_model->rowCount(); if (rowCount == 0) { clearAndHide(); return; } m_current = m_current - 1; if (m_current <= 0) { m_current = rowCount - 1; } activateHint(m_current, rowCount); } void ArgumentHintWidget::activateHint(int i, int rowCount) { const auto index = m_model->index(i); const auto list = index.data(KTextEditor::CodeCompletionModel::CustomHighlight).toList(); const auto highlights = highlightingFromVariantList(list); m_highlighter->formats = highlights; if (rowCount == 1) { m_leftSide->setVisible(false); } else { if (m_leftSide->isHidden()) { m_leftSide->setVisible(true); } m_currentIndicator->setText(QStringLiteral("%1/%2").arg(i + 1).arg(rowCount)); } m_view->setPlainText(index.data().toString()); updateGeometry(); } void ArgumentHintWidget::updateGeometry() { auto block = m_view->document()->begin(); int lines = 0; QFontMetrics fm(m_view->document()->defaultFont()); int maxWidth = 0; while (block.isValid()) { maxWidth = std::max((int)block.layout()->maximumWidth(), maxWidth); lines += block.layout()->lineCount(); block = block.next(); } setFixedHeight((std::max(lines, 1) * fm.height()) + 10 + m_view->document()->documentMargin()); // limit the width to between 400 - 600 int width = std::max(maxWidth, 400); width = std::min(width, 600); setFixedWidth(width); QPoint pos = m_completionWidget->pos(); pos.ry() -= this->height(); pos.ry() -= fm.height(); pos.ry() -= 4; move(pos); } void ArgumentHintWidget::positionAndShow() { updateGeometry(); show(); } void ArgumentHintWidget::clearAndHide() { m_current = -1; m_currentIndicator->clear(); m_view->clear(); hide(); }