From efbbfdbfb542f519b5a875a5fb28abd0acd4def3 Mon Sep 17 00:00:00 2001 From: tetektoza Date: Wed, 12 Nov 2025 00:14:02 +0100 Subject: [PATCH] Gui: Differentiate completion activation modes in Expression Editor Currently if user tries to press TAB during Expression Editor, it inserts both the entry and its first subentry. Also, if user browses the dropdown with arrows keys, it inserts it's values. Root cause of that is the regression made in latest changes to chaining completion logic which is triggered for all completion modes including TAB, which already has its own refresh mechanism. Also, ExpressionTextEdit connected both activated (Enter/click) and highlight signals to the same slot, resulting in arrow key navigation inserting completions. So, this adds separate slots for ExpressionTextEdit to differentiate completion modes. And also updates tab handling to pass ActivationMode::Highlighted to prevent double chaining. --- src/Gui/ExpressionCompleter.cpp | 39 ++++++++++++++++++++++----------- src/Gui/ExpressionCompleter.h | 13 ++++++++--- 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/Gui/ExpressionCompleter.cpp b/src/Gui/ExpressionCompleter.cpp index f10975f1e9..59c53db1c8 100644 --- a/src/Gui/ExpressionCompleter.cpp +++ b/src/Gui/ExpressionCompleter.cpp @@ -1013,7 +1013,7 @@ void ExpressionLineEdit::slotTextChanged(const QString& text) } } -void ExpressionLineEdit::slotCompleteText(const QString& completionPrefix, bool isActivated) +void ExpressionLineEdit::slotCompleteText(const QString& completionPrefix, ActivationMode mode) { int start, end; completer->getPrefixRange(start, end); @@ -1030,7 +1030,7 @@ void ExpressionLineEdit::slotCompleteText(const QString& completionPrefix, bool // chain completions if we select an entry from the completer drop down // and that entry ends with '.' or '#' - if (isActivated) { + if (mode == ActivationMode::Activated) { std::string textToComplete = completionPrefix.toUtf8().constData(); if (textToComplete.size() && (*textToComplete.crbegin() == '.' || *textToComplete.crbegin() == '#')) { @@ -1042,12 +1042,12 @@ void ExpressionLineEdit::slotCompleteText(const QString& completionPrefix, bool void ExpressionLineEdit::slotCompleteTextHighlighted(const QString& completionPrefix) { - slotCompleteText(completionPrefix, false); + slotCompleteText(completionPrefix, ActivationMode::Highlighted); } void ExpressionLineEdit::slotCompleteTextSelected(const QString& completionPrefix) { - slotCompleteText(completionPrefix, true); + slotCompleteText(completionPrefix, ActivationMode::Activated); } @@ -1116,13 +1116,13 @@ void ExpressionTextEdit::setDocumentObject(const App::DocumentObject* currentDoc completer, qOverload(&QCompleter::activated), this, - &ExpressionTextEdit::slotCompleteText + &ExpressionTextEdit::slotCompleteTextSelected ); connect( completer, qOverload(&QCompleter::highlighted), this, - &ExpressionTextEdit::slotCompleteText + &ExpressionTextEdit::slotCompleteTextHighlighted ); connect(this, &ExpressionTextEdit::textChanged2, completer, &ExpressionCompleter::slotUpdate); connect( @@ -1155,7 +1155,7 @@ void ExpressionTextEdit::slotTextChanged() } } -void ExpressionTextEdit::slotCompleteText(const QString& completionPrefix) +void ExpressionTextEdit::slotCompleteText(const QString& completionPrefix, ActivationMode mode) { QTextCursor cursor = textCursor(); int start, end; @@ -1165,17 +1165,31 @@ void ExpressionTextEdit::slotCompleteText(const QString& completionPrefix) cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::MoveAnchor, end - pos); } cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, end - start); + Base::FlagToggler flag(block, false); cursor.insertText(completionPrefix); completer->updatePrefixEnd(cursor.positionInBlock()); - std::string textToComplete = completionPrefix.toUtf8().constData(); - if (textToComplete.size() - && (*textToComplete.crbegin() == '.' || *textToComplete.crbegin() == '#')) { - completer->slotUpdate(cursor.block().text(), cursor.positionInBlock()); + // chain completions only when activated (Enter/Click), not when highlighted (arrow keys) + if (mode == ActivationMode::Activated) { + std::string textToComplete = completionPrefix.toUtf8().constData(); + if (!textToComplete.empty() + && (*textToComplete.crbegin() == '.' || *textToComplete.crbegin() == '#')) { + completer->slotUpdate(cursor.block().text(), cursor.positionInBlock()); + } } } +void ExpressionTextEdit::slotCompleteTextHighlighted(const QString& completionPrefix) +{ + slotCompleteText(completionPrefix, ActivationMode::Highlighted); +} + +void ExpressionTextEdit::slotCompleteTextSelected(const QString& completionPrefix) +{ + slotCompleteText(completionPrefix, ActivationMode::Activated); +} + void ExpressionTextEdit::keyPressEvent(QKeyEvent* e) { Base::FlagToggler flag(block, true); @@ -1208,9 +1222,8 @@ void ExpressionTextEdit::keyPressEvent(QKeyEvent* e) if (!completer->popup()->currentIndex().isValid()) { completer->popup()->setCurrentIndex(completer->popup()->model()->index(0, 0)); } - // insert completion completer->setCurrentRow(completer->popup()->currentIndex().row()); - slotCompleteText(completer->currentCompletion()); + slotCompleteText(completer->currentCompletion(), ActivationMode::Highlighted); // refresh completion list completer->setCompletionPrefix(completer->currentCompletion()); diff --git a/src/Gui/ExpressionCompleter.h b/src/Gui/ExpressionCompleter.h index 7db07b881e..3294aac78e 100644 --- a/src/Gui/ExpressionCompleter.h +++ b/src/Gui/ExpressionCompleter.h @@ -44,6 +44,12 @@ class ObjectIdentifier; namespace Gui { +enum class ActivationMode +{ + Highlighted, // browsing with arrow keys + Activated // confirmed selection (Enter/Click) +}; + class GuiExport ExpressionValidator: public QValidator { Q_OBJECT @@ -123,8 +129,7 @@ Q_SIGNALS: void textChanged2(QString text, int pos); public Q_SLOTS: void slotTextChanged(const QString& text); - // activated == pressed enter on the completion item - void slotCompleteText(const QString& completionPrefix, bool isActivated); + void slotCompleteText(const QString& completionPrefix, ActivationMode mode); void slotCompleteTextHighlighted(const QString& completionPrefix); void slotCompleteTextSelected(const QString& completionPrefix); @@ -159,7 +164,9 @@ Q_SIGNALS: void textChanged2(QString text, int pos); public Q_SLOTS: void slotTextChanged(); - void slotCompleteText(const QString& completionPrefix); + void slotCompleteText(const QString& completionPrefix, ActivationMode mode); + void slotCompleteTextHighlighted(const QString& completionPrefix); + void slotCompleteTextSelected(const QString& completionPrefix); void adjustCompleterToCursor(); private: