Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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<QKeyEvent*>(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;
}
}

Expand All @@ -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()) {
Expand All @@ -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;

Expand All @@ -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;

Expand Down Expand Up @@ -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();
Expand Down