Browse Source

LibGUI: Allow autocomplete to stay open after applying

Previously the autocomplete box would always close after applying a
suggestion. This is the desired behavior in almost all cases, but there
are some situations (like autocompleting paths) where it would be nicer
to keep the autocomplete box open after applying the suggestion.
thislooksfun 3 years ago
parent
commit
d5baf1c1fa

+ 5 - 1
Userland/DevTools/HackStudio/AutoCompleteResponse.h

@@ -22,6 +22,7 @@ inline bool encode(IPC::Encoder& encoder, const GUI::AutocompleteProvider::Entry
     encoder << (u64)response.partial_input_length;
     encoder << (u64)response.partial_input_length;
     encoder << (u32)response.language;
     encoder << (u32)response.language;
     encoder << response.display_text;
     encoder << response.display_text;
+    encoder << (u32)response.hide_autocomplete_after_applying;
     return true;
     return true;
 }
 }
 
 
@@ -30,14 +31,17 @@ inline bool decode(IPC::Decoder& decoder, GUI::AutocompleteProvider::Entry& resp
 {
 {
     u32 language = 0;
     u32 language = 0;
     u64 partial_input_length = 0;
     u64 partial_input_length = 0;
+    u32 hide_autocomplete_after_applying = 0;
     bool ok = decoder.decode(response.completion)
     bool ok = decoder.decode(response.completion)
         && decoder.decode(partial_input_length)
         && decoder.decode(partial_input_length)
         && decoder.decode(language)
         && decoder.decode(language)
-        && decoder.decode(response.display_text);
+        && decoder.decode(response.display_text)
+        && decoder.decode(hide_autocomplete_after_applying);
 
 
     if (ok) {
     if (ok) {
         response.language = static_cast<GUI::AutocompleteProvider::Language>(language);
         response.language = static_cast<GUI::AutocompleteProvider::Language>(language);
         response.partial_input_length = partial_input_length;
         response.partial_input_length = partial_input_length;
+        response.hide_autocomplete_after_applying = static_cast<GUI::AutocompleteProvider::Entry::HideAutocompleteAfterApplying>(hide_autocomplete_after_applying);
     }
     }
 
 
     return ok;
     return ok;

+ 16 - 4
Userland/Libraries/LibGUI/AutocompleteProvider.cpp

@@ -34,6 +34,7 @@ public:
         __ModelRoleCustom = (int)GUI::ModelRole::Custom,
         __ModelRoleCustom = (int)GUI::ModelRole::Custom,
         PartialInputLength,
         PartialInputLength,
         Completion,
         Completion,
+        HideAutocompleteAfterApplying,
     };
     };
 
 
     virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_suggestions.size(); }
     virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override { return m_suggestions.size(); }
@@ -71,6 +72,9 @@ public:
         if ((int)role == InternalRole::Completion)
         if ((int)role == InternalRole::Completion)
             return suggestion.completion;
             return suggestion.completion;
 
 
+        if ((int)role == InternalRole::HideAutocompleteAfterApplying)
+            return suggestion.hide_autocomplete_after_applying == AutocompleteProvider::Entry::HideAutocompleteAfterApplying::Yes;
+
         return {};
         return {};
     }
     }
 
 
@@ -173,21 +177,27 @@ void AutocompleteBox::previous_suggestion()
     }
     }
 }
 }
 
 
-void AutocompleteBox::apply_suggestion()
+AutocompleteProvider::Entry::HideAutocompleteAfterApplying AutocompleteBox::apply_suggestion()
 {
 {
+    auto hide_when_done = AutocompleteProvider::Entry::HideAutocompleteAfterApplying::Yes;
+
     if (m_editor.is_null())
     if (m_editor.is_null())
-        return;
+        return hide_when_done;
 
 
     if (!m_editor->is_editable())
     if (!m_editor->is_editable())
-        return;
+        return hide_when_done;
 
 
     auto selected_index = m_suggestion_view->selection().first();
     auto selected_index = m_suggestion_view->selection().first();
     if (!selected_index.is_valid() || !m_suggestion_view->model()->is_within_range(selected_index))
     if (!selected_index.is_valid() || !m_suggestion_view->model()->is_within_range(selected_index))
-        return;
+        return hide_when_done;
 
 
     auto suggestion_index = m_suggestion_view->model()->index(selected_index.row());
     auto suggestion_index = m_suggestion_view->model()->index(selected_index.row());
     auto completion = suggestion_index.data((GUI::ModelRole)AutocompleteSuggestionModel::InternalRole::Completion).to_string();
     auto completion = suggestion_index.data((GUI::ModelRole)AutocompleteSuggestionModel::InternalRole::Completion).to_string();
     size_t partial_length = suggestion_index.data((GUI::ModelRole)AutocompleteSuggestionModel::InternalRole::PartialInputLength).to_i64();
     size_t partial_length = suggestion_index.data((GUI::ModelRole)AutocompleteSuggestionModel::InternalRole::PartialInputLength).to_i64();
+    auto hide_after_applying = suggestion_index.data((GUI::ModelRole)AutocompleteSuggestionModel::InternalRole::HideAutocompleteAfterApplying).to_bool();
+
+    if (!hide_after_applying)
+        hide_when_done = AutocompleteProvider::Entry::HideAutocompleteAfterApplying::No;
 
 
     VERIFY(completion.length() >= partial_length);
     VERIFY(completion.length() >= partial_length);
     if (!m_editor->has_selection()) {
     if (!m_editor->has_selection()) {
@@ -200,6 +210,8 @@ void AutocompleteBox::apply_suggestion()
     }
     }
 
 
     m_editor->insert_at_cursor_or_replace_selection(completion);
     m_editor->insert_at_cursor_or_replace_selection(completion);
+
+    return hide_when_done;
 }
 }
 
 
 bool AutocompleteProvider::Declaration::operator==(const AutocompleteProvider::Declaration& other) const
 bool AutocompleteProvider::Declaration::operator==(const AutocompleteProvider::Declaration& other) const

+ 7 - 1
Userland/Libraries/LibGUI/AutocompleteProvider.h

@@ -32,6 +32,12 @@ public:
         size_t partial_input_length { 0 };
         size_t partial_input_length { 0 };
         Language language { Language::Unspecified };
         Language language { Language::Unspecified };
         String display_text {};
         String display_text {};
+
+        enum class HideAutocompleteAfterApplying {
+            No,
+            Yes,
+        };
+        HideAutocompleteAfterApplying hide_autocomplete_after_applying { HideAutocompleteAfterApplying::Yes };
     };
     };
 
 
     struct ProjectLocation {
     struct ProjectLocation {
@@ -89,7 +95,7 @@ public:
     bool has_suggestions() { return m_suggestion_view->model()->row_count() > 0; }
     bool has_suggestions() { return m_suggestion_view->model()->row_count() > 0; }
     void next_suggestion();
     void next_suggestion();
     void previous_suggestion();
     void previous_suggestion();
-    void apply_suggestion();
+    AutocompleteProvider::Entry::HideAutocompleteAfterApplying apply_suggestion();
 
 
 private:
 private:
     WeakPtr<TextEditor> m_editor;
     WeakPtr<TextEditor> m_editor;

+ 5 - 2
Userland/Libraries/LibGUI/TextEditor.cpp

@@ -739,8 +739,11 @@ void TextEditor::select_all()
 void TextEditor::keydown_event(KeyEvent& event)
 void TextEditor::keydown_event(KeyEvent& event)
 {
 {
     if (m_autocomplete_box && m_autocomplete_box->is_visible() && (event.key() == KeyCode::Key_Return || event.key() == KeyCode::Key_Tab)) {
     if (m_autocomplete_box && m_autocomplete_box->is_visible() && (event.key() == KeyCode::Key_Return || event.key() == KeyCode::Key_Tab)) {
-        m_autocomplete_box->apply_suggestion();
-        hide_autocomplete();
+        TemporaryChange change { m_should_keep_autocomplete_box, true };
+        if (m_autocomplete_box->apply_suggestion() == AutocompleteProvider::Entry::HideAutocompleteAfterApplying::Yes)
+            hide_autocomplete();
+        else
+            try_update_autocomplete();
         return;
         return;
     }
     }