From 7d84faac66842e2eb299375ade70277a37b7fc11 Mon Sep 17 00:00:00 2001 From: Liu Zhangjian Date: Mon, 21 Oct 2024 19:23:40 +0800 Subject: [PATCH] feat: [completion] Optimize completions display as title Log: Optimize completions display --- .../gui/completion/codecompletionwidget.cpp | 39 ++++++++++++++---- .../gui/completion/codecompletionwidget.h | 14 +++++-- .../codeeditor/gui/private/texteditor_p.cpp | 40 +++++++++++++++---- .../codeeditor/gui/private/texteditor_p.h | 1 + src/plugins/codeeditor/gui/texteditor.cpp | 4 +- 5 files changed, 78 insertions(+), 20 deletions(-) diff --git a/src/plugins/codeeditor/gui/completion/codecompletionwidget.cpp b/src/plugins/codeeditor/gui/completion/codecompletionwidget.cpp index bf21b26d2..fa7e35571 100644 --- a/src/plugins/codeeditor/gui/completion/codecompletionwidget.cpp +++ b/src/plugins/codeeditor/gui/completion/codecompletionwidget.cpp @@ -356,27 +356,43 @@ void CodeCompletionWidget::updateHeight() } geom.setHeight(baseHeight); - if (geometry() != geom) + if (geometry() != geom) { setGeometry(geom); + if (completionOrigin == Top) + updatePosition(); + } QSize entryListSize = QSize(completionView->width(), baseHeight - 2 * frameWidth()); if (completionView->size() != entryListSize) completionView->resize(entryListSize); } -void CodeCompletionWidget::updatePosition(bool force) +void CodeCompletionWidget::updatePosition(bool force, CompletionOrigin origin) { if (!force && !isCompletionActive()) return; - auto cursorPosition = editor()->pointFromPosition(editor()->cursorPosition()); - if (cursorPosition == QPoint(-1, -1)) { - abortCompletion(); - return; + if (origin != completionOrigin) + completionOrigin = origin; + + if (showPosition == QPoint(-1, -1)) { + showPosition = editor()->pointFromPosition(editor()->cursorPosition()); + if (showPosition == QPoint(-1, -1)) { + abortCompletion(); + return; + } + } + + QPoint p = editor()->mapToGlobal(showPosition); + switch (origin) { + case Bottom: + p.setY(p.y() + editor()->fontMetrics().height() + 2); + break; + case Top: + p.setY(p.y() - height()); + break; } - QPoint p = editor()->mapToGlobal(cursorPosition); - p.setY(p.y() + editor()->fontMetrics().height() + 2); move(p); } @@ -478,6 +494,13 @@ void CodeCompletionWidget::focusOutEvent(QFocusEvent *event) abortCompletion(); } +void CodeCompletionWidget::hideEvent(QHideEvent *event) +{ + showPosition = QPoint(-1, -1); + completionOrigin = Bottom; + QFrame::hideEvent(event); +} + void CodeCompletionWidget::onTextAdded(int pos, int len, int added, const QString &text, int line) { Q_UNUSED(line) diff --git a/src/plugins/codeeditor/gui/completion/codecompletionwidget.h b/src/plugins/codeeditor/gui/completion/codecompletionwidget.h index 4cb37ba72..7e1b95430 100644 --- a/src/plugins/codeeditor/gui/completion/codecompletionwidget.h +++ b/src/plugins/codeeditor/gui/completion/codecompletionwidget.h @@ -21,6 +21,11 @@ class CodeCompletionWidget : public QFrame { Q_OBJECT public: + enum CompletionOrigin { + Bottom = 0, + Top + }; + explicit CodeCompletionWidget(TextEditor *parent); TextEditor *editor() const; @@ -29,7 +34,7 @@ class CodeCompletionWidget : public QFrame void startCompletion(); void updateHeight(); - void updatePosition(bool force = false); + void updatePosition(bool force = false, CompletionOrigin origin = Bottom); void setCompletion(const QString &info, const QIcon &icon, const QKeySequence &key); public slots: @@ -40,7 +45,8 @@ public slots: void automaticInvocation(); protected: - virtual void focusOutEvent(QFocusEvent *event) override; + void focusOutEvent(QFocusEvent *event) override; + void hideEvent(QHideEvent *event) override; private: void initUI(); @@ -68,14 +74,14 @@ private slots: CodeCompletionModel *completionModel { nullptr }; CompletionSortFilterProxyModel *proxyModel { nullptr }; CodeCompletionExtendWidget *completionExtWidget { nullptr }; - QTimer *automaticInvocationTimer { nullptr }; QString automaticInvocationLine; int automaticInvocationAt; - bool needShow { false }; bool isCompletionInput { false }; + QPoint showPosition { -1, -1 }; + CompletionOrigin completionOrigin { Bottom }; }; #endif // CODECOMPLETIONWIDGET_H diff --git a/src/plugins/codeeditor/gui/private/texteditor_p.cpp b/src/plugins/codeeditor/gui/private/texteditor_p.cpp index c4a5b8545..9e7ad5586 100644 --- a/src/plugins/codeeditor/gui/private/texteditor_p.cpp +++ b/src/plugins/codeeditor/gui/private/texteditor_p.cpp @@ -57,6 +57,7 @@ void TextEditorPrivate::init() TextEditor::SC_CASEINSENSITIVEBEHAVIOUR_IGNORECASE); selectionChangeTimer.setSingleShot(true); selectionChangeTimer.setInterval(50); + completionWidget->installEventFilter(q); initWidgetContainer(); initMargins(); @@ -249,13 +250,7 @@ void TextEditorPrivate::setInlineCompletion() cancelInlineCompletion(); inlineCompletionCache = qMakePair(pos.line, item.completion); - const auto &part1 = item.completion.mid(0, item.completion.indexOf('\n')); - const auto &part2 = item.completion.mid(item.completion.indexOf('\n') + 1); - QsciStyle cpStyle(1, "", Qt::gray, q->lexer() ? q->lexer()->defaultPaper(-1) : q->paper(), - q->lexer() ? q->lexer()->defaultFont() : q->font()); - q->eOLAnnotate(pos.line, part1, cpStyle); - if (part1 != part2) - q->annotate(pos.line, part2, cpStyle); + updateInlineCompletion(); return; } } @@ -652,6 +647,37 @@ void TextEditorPrivate::cancelInlineCompletion() inlineCompletionCache = qMakePair(-1, QString()); } +void TextEditorPrivate::updateInlineCompletion() +{ + int line = inlineCompletionCache.first; + QString completion = inlineCompletionCache.second; + if (line == -1) + return; + + const auto &part1 = completion.mid(0, completion.indexOf('\n')); + QString part2 = completion.mid(completion.indexOf('\n') + 1); + QsciStyle cpStyle(1, "", Qt::gray, q->lexer() ? q->lexer()->defaultPaper(-1) : q->paper(), + q->lexer() ? q->lexer()->defaultFont() : q->font()); + q->eOLAnnotate(line, part1, cpStyle); + if (part1 != part2) { + if (!completionWidget->isCompletionActive()) { + q->annotate(line, part2, cpStyle); + return; + } + + auto point = q->pointFromPosition(q->cursorPosition()); + if (point.y() >= completionWidget->height()) { + completionWidget->updatePosition(true, CodeCompletionWidget::Top); + } else { + auto lineHeight = q->textHeight(line); + auto padding = completionWidget->height() / lineHeight; + part2.prepend(QString(padding, '\n')); + } + + q->annotate(line, part2, cpStyle); + } +} + void TextEditorPrivate::resetThemeColor() { if (q->lexer()) { diff --git a/src/plugins/codeeditor/gui/private/texteditor_p.h b/src/plugins/codeeditor/gui/private/texteditor_p.h index 146e6c2d0..afde07ed8 100644 --- a/src/plugins/codeeditor/gui/private/texteditor_p.h +++ b/src/plugins/codeeditor/gui/private/texteditor_p.h @@ -73,6 +73,7 @@ class TextEditorPrivate : public QObject void provideInlineCompletion(int pos, int added); void applyInlineCompletion(); void cancelInlineCompletion(); + void updateInlineCompletion(); public slots: void resetThemeColor(); diff --git a/src/plugins/codeeditor/gui/texteditor.cpp b/src/plugins/codeeditor/gui/texteditor.cpp index 0065792dc..2d9bde449 100644 --- a/src/plugins/codeeditor/gui/texteditor.cpp +++ b/src/plugins/codeeditor/gui/texteditor.cpp @@ -850,7 +850,7 @@ void TextEditor::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Tab && d->inlineCompletionCache.first != -1) return d->applyInlineCompletion(); - + if (event->key() == Qt::Key_Escape && d->lineWidgetContainer->isVisible()) return closeLineWidget(); @@ -897,6 +897,8 @@ bool TextEditor::eventFilter(QObject *obj, QEvent *event) d->updateLineWidgetPosition(); } else if (obj == d->mainWindow() && event->type() == QEvent::Move) { d->updateLineWidgetPosition(); + } else if (obj == d->completionWidget && event->type() == QEvent::Hide) { + d->updateInlineCompletion(); } return QsciScintilla::eventFilter(obj, event);