[education/labplot] src/frontend/script: [scripting] removed custom completion popup, KTextEditor's built-in logic is enough, and added a customer action to bring up the completion again.
Alexander Semke <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit e155a7f3ef47d9cd6d8444d420316afc4075ccde by Alexander Semke.
Committed on 01/08/2026 at 20:22.
Pushed by asemke into branch 'master'.
[scripting] removed custom completion popup, KTextEditor's built-in logic is enough, and added a customer action to bring up the completion again.
M +0 -5 src/frontend/script/ScriptCompletionModel.cpp
M +0 -4 src/frontend/script/ScriptCompletionModel.h
M +18 -153 src/frontend/script/ScriptEditor.cpp
M +1 -7 src/frontend/script/ScriptEditor.h
https://invent.kde.org/education/labplot/-/commit/e155a7f3ef47d9cd6d8444d420316afc4075ccde
diff --git a/src/frontend/script/ScriptCompletionModel.cpp b/src/frontend/script/ScriptCompletionModel.cpp
index 0ae5528a54..78cbd28a9b 100644
--- a/src/frontend/script/ScriptCompletionModel.cpp
+++ b/src/frontend/script/ScriptCompletionModel.cpp
@@ -226,11 +226,6 @@ void ScriptCompletionModel::startCompletionRequest() {
setRowCount(m_matches.size());
endResetModel();
-
- // Emit signal for custom popup
- Q_EMIT modelIsReady(m_matches);
-
- // DEBUG(Q_FUNC_INFO << ", Showing " << m_matches.size() << " completions for prefix '" << prefix << "'")
}
QVariant ScriptCompletionModel::data(const QModelIndex& index, int role) const {
diff --git a/src/frontend/script/ScriptCompletionModel.h b/src/frontend/script/ScriptCompletionModel.h
index 8089b18835..9ea9407c62 100644
--- a/src/frontend/script/ScriptCompletionModel.h
+++ b/src/frontend/script/ScriptCompletionModel.h
@@ -46,10 +46,6 @@ public:
void abortCompletion();
bool initPylabplotSymbols();
- const QList<CompletionItem>& matches() const { return m_matches; }
-
-Q_SIGNALS:
- void modelIsReady(const QList<CompletionItem>&);
private Q_SLOTS:
void startCompletionRequest();
diff --git a/src/frontend/script/ScriptEditor.cpp b/src/frontend/script/ScriptEditor.cpp
index 6afe551cc8..691561472a 100644
--- a/src/frontend/script/ScriptEditor.cpp
+++ b/src/frontend/script/ScriptEditor.cpp
@@ -35,8 +35,6 @@
#include <QTextCharFormat>
#include <QClipboard>
#include <QApplication>
-#include <QListWidget>
-#include <QKeyEvent>
ScriptEditor::ScriptEditor(Script* script, QWidget* parent)
: QWidget(parent), m_script(script) {
@@ -77,8 +75,24 @@ ScriptEditor::ScriptEditor(Script* script, QWidget* parent)
m_kTextEditorView->registerCompletionModel(m_completionModel);
m_kTextEditorView->setAutomaticInvocationEnabled(true);
- // Connect to show custom popup (no parameter needed - we'll query the model)
- connect(m_completionModel, &ScriptCompletionModel::modelIsReady, this, &ScriptEditor::showCustomCompleter);
+ // Setup manual code completion shortcut
+ // Use Ctrl+Shift+Space on macOS (Ctrl+Space conflicts with keyboard language switching)
+ // Use Ctrl+Space on other platforms (standard KTextEditor shortcut)
+ m_codeCompletionAction = new QAction(QStringLiteral("Code Completion"), this);
+#ifdef Q_OS_MACOS
+ m_codeCompletionAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Space);
+#else
+ m_codeCompletionAction->setShortcut(Qt::CTRL | Qt::Key_Space);
+#endif
+ m_codeCompletionAction->setWhatsThis(QStringLiteral("Manually trigger code completion"));
+ connect(m_codeCompletionAction, &QAction::triggered, [this]() {
+ if (m_completionModel && m_kTextEditorView) {
+ auto cursor = m_kTextEditorView->cursorPosition();
+ auto wordRange = m_kTextEditorView->document()->wordRangeAt(cursor);
+ m_kTextEditorView->startCompletion(wordRange, m_completionModel);
+ }
+ });
+ m_kTextEditorView->addAction(m_codeCompletionAction);
}
ScriptEditor::~ScriptEditor() {
@@ -354,152 +368,3 @@ void ScriptEditor::showOutputContextMenu(const QPoint& pos) {
menu.exec(ui.output->mapToGlobal(pos));
}
-
-void ScriptEditor::showCustomCompleter() {
- if (!m_completionModel)
- return;
-
- // Get matches from the completion model
- const auto& matches = m_completionModel->matches();
-
- // Hide completer if no matches
- if (matches.isEmpty()) {
- if (m_customCompleter && m_customCompleter->isVisible())
- m_customCompleter->hide();
- return;
- }
-
- // Create completer widget on first use
- if (!m_customCompleter) {
- m_customCompleter = new QListWidget();
- m_customCompleter->setWindowFlags(Qt::Popup);
- m_customCompleter->setFocusPolicy(Qt::NoFocus);
- m_customCompleter->setFocusProxy(m_kTextEditorView);
- m_customCompleter->installEventFilter(this);
-
- connect(m_customCompleter, &QListWidget::itemActivated, this, &ScriptEditor::onCompleterItemSelected);
- connect(m_customCompleter, &QListWidget::itemClicked, this, &ScriptEditor::onCompleterItemSelected);
- }
-
- // Populate with matches
- m_customCompleter->clear();
- for (const auto& item : matches) {
- QString displayText = item.name;
- // Add icon or indicator for type
- if (item.isClass)
- displayText += QStringLiteral(" [class]");
- else if (item.isFunction)
- displayText += QStringLiteral(" [function]");
- else if (item.isVariable)
- displayText += QStringLiteral(" [variable]");
-
- auto* listItem = new QListWidgetItem(displayText, m_customCompleter);
- listItem->setData(Qt::UserRole, item.name); // Store clean name for insertion
-
- // Set tooltip with signature and docstring if available
- QString tooltip;
- if (!item.signature.isEmpty())
- tooltip = item.signature;
- else if (item.isFunction || item.isClass)
- tooltip = item.name + QStringLiteral("()");
-
- if (!item.docstring.isEmpty()) {
- if (!tooltip.isEmpty())
- tooltip += QStringLiteral("\n\n");
- tooltip += item.docstring;
- }
-
- if (!tooltip.isEmpty())
- listItem->setToolTip(tooltip);
- }
- m_customCompleter->setCurrentRow(0);
-
- // Position below cursor
- KTextEditor::Cursor cursor = m_kTextEditorView->cursorPosition();
- QPoint cursorPos = m_kTextEditorView->cursorPositionCoordinates();
- QPoint globalPos = m_kTextEditorView->mapToGlobal(cursorPos);
-
- // Move below cursor line
- globalPos.setY(globalPos.y() + m_kTextEditorView->fontMetrics().height());
-
- m_customCompleter->move(globalPos);
- m_customCompleter->setMinimumWidth(200);
- m_customCompleter->setMaximumHeight(200);
- m_customCompleter->show();
-}
-
-void ScriptEditor::onCompleterItemSelected() {
- if (!m_customCompleter || !m_kTextEditorView)
- return;
-
- QListWidgetItem* item = m_customCompleter->currentItem();
- if (!item)
- return;
-
- // Get clean name from UserRole data
- QString textToInsert = item->data(Qt::UserRole).toString();
-
- // Check if it's a function or class by looking at the display text
- QString displayText = item->text();
- bool isFunction = displayText.contains(QStringLiteral("[function]"));
- bool isClass = displayText.contains(QStringLiteral("[class]"));
-
- // Add parentheses for functions and classes
- if (isFunction || isClass)
- textToInsert += QStringLiteral("()");
-
- // Replace current word with completion
- KTextEditor::Cursor currentPos = m_kTextEditorView->cursorPosition();
- KTextEditor::Range wordRange = m_kTextEditorView->document()->wordRangeAt(currentPos);
- m_kTextEditorView->document()->replaceText(wordRange, textToInsert);
-
- // Position cursor inside parentheses for functions/classes
- if (isFunction || isClass) {
- KTextEditor::Cursor newCursorPos = wordRange.start();
- newCursorPos.setColumn(newCursorPos.column() + textToInsert.length() - 1); // -1 to be inside ()
- m_kTextEditorView->setCursorPosition(newCursorPos);
- }
-
- // Hide completer
- m_customCompleter->hide();
-
- // Return focus to editor
- m_kTextEditorView->setFocus();
-}
-
-bool ScriptEditor::eventFilter(QObject* obj, QEvent* event) {
- if (obj == m_customCompleter && m_customCompleter->isVisible()) {
- if (event->type() == QEvent::KeyPress) {
- auto* keyEvent = static_cast<QKeyEvent*>(event);
- switch (keyEvent->key()) {
- case Qt::Key_Return:
- case Qt::Key_Enter:
- // Accept current selection
- onCompleterItemSelected();
- return true;
-
- case Qt::Key_Escape:
- // Hide completer
- m_customCompleter->hide();
- m_kTextEditorView->setFocus();
- return true;
-
- case Qt::Key_Up:
- case Qt::Key_Down:
- case Qt::Key_PageUp:
- case Qt::Key_PageDown:
- // Let the list widget handle navigation
- return false;
-
- default:
- // Pass other keys to editor and hide completer
- m_customCompleter->hide();
- m_kTextEditorView->setFocus();
- QApplication::sendEvent(m_kTextEditorView, event);
- return true;
- }
- }
- }
-
- return QWidget::eventFilter(obj, event);
-}
diff --git a/src/frontend/script/ScriptEditor.h b/src/frontend/script/ScriptEditor.h
index eb5f19ae0d..aa2f8f892a 100644
--- a/src/frontend/script/ScriptEditor.h
+++ b/src/frontend/script/ScriptEditor.h
@@ -19,7 +19,6 @@ class QMenu;
class Script;
class QToolBar;
class QToolButton;
-class QListWidget;
class ScriptCompletionModel;
namespace KTextEditor{
@@ -41,14 +40,9 @@ public:
void registerShortcuts();
void unregisterShortcuts();
-protected:
- bool eventFilter(QObject* obj, QEvent* event) override;
-
private Q_SLOTS:
void handleAnchorClicked(const QUrl&);
void showOutputContextMenu(const QPoint&);
- void showCustomCompleter();
- void onCompleterItemSelected();
public Q_SLOTS:
void createContextMenu(QMenu*);
@@ -63,8 +57,8 @@ private:
QAction* m_clearOutputAction{nullptr};
QAction* m_copySelectedAction{nullptr};
QAction* m_copyAllOutputAction{nullptr};
+ QAction* m_codeCompletionAction{nullptr};
ScriptCompletionModel* m_completionModel{nullptr};
- QListWidget* m_customCompleter{nullptr};
void initActions();
void initMenus();