diff --git a/CHANGELOG.md b/CHANGELOG.md index d80e095..862bf97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Changelog +- (2026-09-08) Fix autocomplete stealing keyboard focus from the editor. The popup and its list can no longer receive focus; typed text and navigation keys are routed between the editor and the list via the event filter (fixes Mudlet/Mudlet#5310) + - (2026-04-14) #177, Fix strange mouse behavior caused by rawLineIndexForYpos returning std::npos with negative y positions. (@distractor) - (2026-04-01) #176, Fix FreeBSD Build, CMake find Oniguruma, cmake fixes. (@SlySven) diff --git a/edbee-lib/edbee/views/components/texteditorautocompletecomponent.cpp b/edbee-lib/edbee/views/components/texteditorautocompletecomponent.cpp index 3c23332..61110e6 100644 --- a/edbee-lib/edbee/views/components/texteditorautocompletecomponent.cpp +++ b/edbee-lib/edbee/views/components/texteditorautocompletecomponent.cpp @@ -50,10 +50,15 @@ TextEditorAutoCompleteComponent::TextEditorAutoCompleteComponent(TextEditorContr this->setAttribute(Qt::WA_ShowWithoutActivating); menuRef_ = new QMenu(this); + menuRef_->setFocusPolicy(Qt::NoFocus); + menuRef_->setAttribute(Qt::WA_ShowWithoutActivating); menuRef_->setAccessibleName("Autocomplete"); listWidgetRef_ = new QListWidget(menuRef_); + listWidgetRef_->setFocusPolicy(Qt::NoFocus); + listWidgetRef_->setAttribute(Qt::WA_ShowWithoutActivating); + editorComponentRef_->installEventFilter(this); listWidgetRef_->installEventFilter(this); menuRef_->installEventFilter(this); @@ -336,15 +341,20 @@ bool TextEditorAutoCompleteComponent::eventFilter(QObject *obj, QEvent *event) return QObject::eventFilter(obj, event); } - if(obj == listWidgetRef_ && event->type() == QEvent::KeyPress) { + if ((obj == listWidgetRef_ || obj == editorComponentRef_) && event->type() == QEvent::KeyPress && menuRef_->isVisible()) { QKeyEvent* key = static_cast(event); + const bool eventFromEditor = (obj == editorComponentRef_); // text keys are allowed if (!key->text().isEmpty()) { QChar nextChar = key->text().at(0); if (nextChar.isLetterOrNumber()) { - QApplication::sendEvent(editorComponentRef_, event); - return true; + if (eventFromEditor) { + // the editor handles typed text itself + return false; + } + QApplication::sendEvent(editorComponentRef_, event); + return true; } } @@ -360,6 +370,9 @@ bool TextEditorAutoCompleteComponent::eventFilter(QObject *obj, QEvent *event) case Qt::Key_Tab: if (listWidgetRef_->currentItem() && currentWord_ == listWidgetRef_->currentItem()->text()) { // sends normal enter/return/tab if you've typed a full word menuRef_->close(); + if (eventFromEditor) { + return false; + } QApplication::sendEvent(editorComponentRef_, event); return true; } else if (listWidgetRef_->currentItem()) { @@ -371,10 +384,16 @@ bool TextEditorAutoCompleteComponent::eventFilter(QObject *obj, QEvent *event) break; case Qt::Key_Backspace: + if (eventFromEditor) { + return false; + } QApplication::sendEvent(editorComponentRef_, event); return true; case Qt::Key_Shift: //ignore shift, don't hide + if (eventFromEditor) { + return false; + } QApplication::sendEvent(editorComponentRef_, event); return true; @@ -383,11 +402,18 @@ bool TextEditorAutoCompleteComponent::eventFilter(QObject *obj, QEvent *event) case Qt::Key_Down: case Qt::Key_PageDown: case Qt::Key_PageUp: + if (eventFromEditor) { + QApplication::sendEvent(listWidgetRef_, event); + return true; + } return false; } // default operation is to hide and continue the event menuRef_->close(); + if (eventFromEditor) { + return false; + } QApplication::sendEvent(editorComponentRef_, event); return true; @@ -428,7 +454,6 @@ void TextEditorAutoCompleteComponent::updateList() // fills the autocomplete list with the curent word if (fillAutoCompleteList(doc, range, currentWord_)) { menuRef_->popup(menuRef_->pos()); - listWidgetRef_->setFocus(); // position the widget showInfoTip();