UI/Qt: Preview non-prefix autocomplete suggestions

Allow highlighted rows that are not valid inline completions to preview
their full suggestion text in the location editor. This covers history
entries matched by title or URL substring, while ordinary typing stays
on the inline-only path.

Keep automatic inline autocomplete text when Escape closes the popup.
Escape still cancels hovered or keyboard-selected preview text.
This commit is contained in:
Andreas Kling 2026-06-21 11:57:28 +02:00 committed by Andreas Kling
parent 3fcd9d83a1
commit 2f0fe26ce9
2 changed files with 99 additions and 25 deletions

View file

@ -256,12 +256,20 @@ LocationEdit::LocationEdit(QWidget* parent)
if (!hasFocus()) if (!hasFocus())
return; return;
auto query = current_query(); auto query = autocomplete_query();
int selected_row = -1; int selected_row = -1;
if (!m_autocomplete_query_without_inline.isNull() && text() == m_autocomplete_query_without_inline) if (!m_autocomplete_preview_query.isNull()) {
for (size_t i = 0; i < suggestions.size(); ++i) {
if (qstring_from_ak_string(suggestions[i].text) == text()) {
selected_row = static_cast<int>(i);
break;
}
}
} else if (!m_autocomplete_query_without_inline.isNull() && text() == m_autocomplete_query_without_inline) {
selected_row = 0; selected_row = 0;
else } else {
selected_row = apply_inline_autocomplete(suggestions); selected_row = apply_inline_autocomplete(suggestions);
}
// Do not update the popup while results are still changing. // Do not update the popup while results are still changing.
// Intermediate updates are triggered on every keystroke and would // Intermediate updates are triggered on every keystroke and would
@ -286,22 +294,29 @@ LocationEdit::LocationEdit(QWidget* parent)
}; };
connect(m_autocomplete, &Autocomplete::suggestion_activated, this, [this](QString const& text) { connect(m_autocomplete, &Autocomplete::suggestion_activated, this, [this](QString const& text) {
m_is_applying_inline_autocomplete = true; m_autocomplete_preview_query = QString();
setText(text); m_current_inline_autocomplete_suggestion.clear();
m_is_applying_inline_autocomplete = false; set_text_without_inline_autocomplete(text);
m_autocomplete->close(); m_autocomplete->close();
emit returnPressed(); emit returnPressed();
}); });
connect(m_autocomplete, &Autocomplete::suggestion_highlighted, this, [this](QString const& text) { connect(m_autocomplete, &Autocomplete::suggestion_highlighted, this, [this](QString const& text) {
auto query = current_query(); m_has_highlighted_autocomplete_suggestion = true;
apply_inline_autocomplete_suggestion_text(text, query); auto query = autocomplete_query();
apply_inline_autocomplete_suggestion_text(text, query, true);
}); });
connect(m_autocomplete, &Autocomplete::did_close, this, [this] { connect(m_autocomplete, &Autocomplete::did_close, this, [this] {
auto should_preserve_inline_autocomplete = m_should_preserve_inline_autocomplete_on_close;
auto should_restore_query = should_restore_autocomplete_query();
m_should_preserve_inline_autocomplete_on_close = false;
m_has_highlighted_autocomplete_suggestion = false;
m_current_inline_autocomplete_suggestion.clear(); m_current_inline_autocomplete_suggestion.clear();
if (should_preserve_inline_autocomplete)
return;
if (!m_autocomplete_query_without_inline.isNull()) { if (!m_autocomplete_query_without_inline.isNull()) {
if (hasSelectedText()) if (should_restore_query)
restore_query(); restore_query();
m_autocomplete_query_without_inline = QString(); m_autocomplete_query_without_inline = QString();
return; return;
@ -330,6 +345,8 @@ LocationEdit::LocationEdit(QWidget* parent)
return; return;
m_autocomplete_query_without_inline = QString(); m_autocomplete_query_without_inline = QString();
m_autocomplete_preview_query = QString();
m_has_highlighted_autocomplete_suggestion = false;
if (m_url_is_hidden) if (m_url_is_hidden)
m_has_user_edited_hidden_url = true; m_has_user_edited_hidden_url = true;
@ -460,12 +477,10 @@ void LocationEdit::focusOutEvent(QFocusEvent* event)
animate_focus_glow(0); animate_focus_glow(0);
if (hasSelectedText()) { if (should_restore_autocomplete_query()) {
auto query = current_query(); auto query = current_query();
m_is_applying_inline_autocomplete = true; set_text_without_inline_autocomplete(query);
setText(query);
setCursorPosition(query.length()); setCursorPosition(query.length());
m_is_applying_inline_autocomplete = false;
} }
auto should_cancel_pending_query = !m_should_skip_autocomplete_cancel_on_focus_out; auto should_cancel_pending_query = !m_should_skip_autocomplete_cancel_on_focus_out;
@ -512,8 +527,17 @@ void LocationEdit::animate_focus_glow(int target_alpha)
void LocationEdit::keyPressEvent(QKeyEvent* event) void LocationEdit::keyPressEvent(QKeyEvent* event)
{ {
if (event->key() == Qt::Key_Escape) { if (event->key() == Qt::Key_Escape) {
if (m_autocomplete->close()) if (m_autocomplete->is_visible()
&& m_autocomplete_query_without_inline.isNull()
&& m_autocomplete_preview_query.isNull()
&& !m_has_highlighted_autocomplete_suggestion
&& hasSelectedText()) {
m_should_preserve_inline_autocomplete_on_close = true;
}
if (m_autocomplete->close()) {
m_autocomplete->cancel_pending_query();
return; return;
}
reset_autocomplete_state(); reset_autocomplete_state();
if (m_url.has_value()) if (m_url.has_value())
setText(serialized_url()); setText(serialized_url());
@ -532,7 +556,7 @@ void LocationEdit::keyPressEvent(QKeyEvent* event)
} }
if ((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && m_autocomplete->is_visible()) { if ((event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) && m_autocomplete->is_visible()) {
auto query = current_query(); auto query = autocomplete_query();
if (m_autocomplete_popup_query == query) { if (m_autocomplete_popup_query == query) {
activate_selected_autocomplete_suggestion(); activate_selected_autocomplete_suggestion();
event->accept(); event->accept();
@ -952,6 +976,9 @@ QString LocationEdit::display_url() const
QString LocationEdit::current_query() const QString LocationEdit::current_query() const
{ {
if (!m_autocomplete_preview_query.isNull())
return m_autocomplete_preview_query;
if (!hasSelectedText()) if (!hasSelectedText())
return text(); return text();
int start = selectionStart(); int start = selectionStart();
@ -1070,7 +1097,7 @@ int LocationEdit::apply_inline_autocomplete(Vector<WebView::AutocompleteSuggesti
return 0; return 0;
} }
bool LocationEdit::apply_inline_autocomplete_suggestion_text(QString const& suggestion_text, QString const& query) bool LocationEdit::apply_inline_autocomplete_suggestion_text(QString const& suggestion_text, QString const& query, bool allow_preview)
{ {
if (suggestion_matches_query_exactly(query, suggestion_text)) { if (suggestion_matches_query_exactly(query, suggestion_text)) {
restore_query(); restore_query();
@ -1079,6 +1106,10 @@ bool LocationEdit::apply_inline_autocomplete_suggestion_text(QString const& sugg
} }
auto inline_text = inline_autocomplete_text_for_suggestion(query, suggestion_text); auto inline_text = inline_autocomplete_text_for_suggestion(query, suggestion_text);
if (inline_text.isEmpty() && allow_preview) {
apply_autocomplete_preview_text(suggestion_text, query);
return true;
}
if (inline_text.isEmpty()) if (inline_text.isEmpty())
return false; return false;
@ -1102,18 +1133,33 @@ void LocationEdit::apply_inline_autocomplete_text(QString const& inline_text, QS
&& selectedText().length() == completion_length) && selectedText().length() == completion_length)
return; return;
m_is_applying_inline_autocomplete = true; m_autocomplete_preview_query = QString();
setText(inline_text); set_text_without_inline_autocomplete(inline_text);
setSelection(completion_start, completion_length); setSelection(completion_start, completion_length);
m_is_applying_inline_autocomplete = false; }
void LocationEdit::apply_autocomplete_preview_text(QString const& suggestion_text, QString const& query)
{
if (!hasFocus())
return;
if (text() == suggestion_text && hasSelectedText()
&& selectionStart() == 0
&& selectedText().length() == suggestion_text.length())
return;
m_current_inline_autocomplete_suggestion.clear();
m_autocomplete_preview_query = query;
set_text_without_inline_autocomplete(suggestion_text);
selectAll();
} }
void LocationEdit::activate_selected_autocomplete_suggestion() void LocationEdit::activate_selected_autocomplete_suggestion()
{ {
if (auto selected = m_autocomplete->selected_suggestion(); selected.has_value()) { if (auto selected = m_autocomplete->selected_suggestion(); selected.has_value()) {
m_is_applying_inline_autocomplete = true; m_autocomplete_preview_query = QString();
setText(qstring_from_ak_string(*selected)); m_current_inline_autocomplete_suggestion.clear();
m_is_applying_inline_autocomplete = false; set_text_without_inline_autocomplete(qstring_from_ak_string(*selected));
} }
m_autocomplete->close(); m_autocomplete->close();
emit returnPressed(); emit returnPressed();
@ -1128,17 +1174,38 @@ void LocationEdit::restore_query()
if (text() == query && !hasSelectedText()) if (text() == query && !hasSelectedText())
return; return;
m_is_applying_inline_autocomplete = true; set_text_without_inline_autocomplete(query);
setText(query);
setCursorPosition(query.length()); setCursorPosition(query.length());
m_autocomplete_preview_query = QString();
}
void LocationEdit::set_text_without_inline_autocomplete(QString const& text)
{
m_is_applying_inline_autocomplete = true;
setText(text);
m_is_applying_inline_autocomplete = false; m_is_applying_inline_autocomplete = false;
} }
bool LocationEdit::should_restore_autocomplete_query() const
{
return !m_autocomplete_preview_query.isNull() || !m_current_inline_autocomplete_suggestion.isEmpty();
}
QString LocationEdit::autocomplete_query() const
{
if (!m_autocomplete_query_without_inline.isNull())
return m_autocomplete_query_without_inline;
return current_query();
}
void LocationEdit::reset_autocomplete_state() void LocationEdit::reset_autocomplete_state()
{ {
m_autocomplete_popup_query = QString(); m_autocomplete_popup_query = QString();
m_autocomplete_query_without_inline = QString(); m_autocomplete_query_without_inline = QString();
m_autocomplete_preview_query = QString();
m_current_inline_autocomplete_suggestion.clear(); m_current_inline_autocomplete_suggestion.clear();
m_has_highlighted_autocomplete_suggestion = false;
m_should_preserve_inline_autocomplete_on_close = false;
m_should_skip_autocomplete_cancel_on_focus_out = false; m_should_skip_autocomplete_cancel_on_focus_out = false;
m_pending_autocomplete_activation_query = QString(); m_pending_autocomplete_activation_query = QString();
m_suppressed_inline_autocomplete_query = QString(); m_suppressed_inline_autocomplete_query = QString();

View file

@ -73,10 +73,14 @@ private:
QString display_url() const; QString display_url() const;
int apply_inline_autocomplete(Vector<WebView::AutocompleteSuggestion> const&); int apply_inline_autocomplete(Vector<WebView::AutocompleteSuggestion> const&);
bool apply_inline_autocomplete_suggestion_text(QString const& suggestion_text, QString const& query); bool apply_inline_autocomplete_suggestion_text(QString const& suggestion_text, QString const& query, bool allow_preview = false);
void apply_inline_autocomplete_text(QString const& inline_text, QString const& query); void apply_inline_autocomplete_text(QString const& inline_text, QString const& query);
void apply_autocomplete_preview_text(QString const& suggestion_text, QString const& query);
void activate_selected_autocomplete_suggestion(); void activate_selected_autocomplete_suggestion();
void restore_query(); void restore_query();
void set_text_without_inline_autocomplete(QString const& text);
bool should_restore_autocomplete_query() const;
QString autocomplete_query() const;
QString current_query() const; QString current_query() const;
void reset_autocomplete_state(); void reset_autocomplete_state();
@ -99,9 +103,12 @@ private:
bool m_is_applying_inline_autocomplete { false }; bool m_is_applying_inline_autocomplete { false };
bool m_should_suppress_inline_autocomplete_on_next_change { false }; bool m_should_suppress_inline_autocomplete_on_next_change { false };
bool m_has_highlighted_autocomplete_suggestion { false };
bool m_should_preserve_inline_autocomplete_on_close { false };
bool m_should_skip_autocomplete_cancel_on_focus_out { false }; bool m_should_skip_autocomplete_cancel_on_focus_out { false };
QString m_autocomplete_popup_query; QString m_autocomplete_popup_query;
QString m_autocomplete_query_without_inline; QString m_autocomplete_query_without_inline;
QString m_autocomplete_preview_query;
QString m_current_inline_autocomplete_suggestion; QString m_current_inline_autocomplete_suggestion;
QString m_pending_autocomplete_activation_query; QString m_pending_autocomplete_activation_query;
QString m_suppressed_inline_autocomplete_query; QString m_suppressed_inline_autocomplete_query;