From df3594bd53c71d06f6e103f6782bb26b970beac4 Mon Sep 17 00:00:00 2001 From: Jules Aguillon Date: Thu, 19 Feb 2026 00:37:42 +0100 Subject: Autocomplete on space bar and undo on delete (#1179) * Refactor: Implement the space key as an editing action The space key is no longer a CHAR key but now an EDITING key. This allows changing the behavior of the space bar while allowing custom layout users to define a key outputing a space as before. KeyModifier and ComposeKey are updated to treat the space key as before. * Enter the best suggestion when the space bar is pressed * Refactor: Implement backspace as an editing action * Undo suggestion when delete is pressed The delete key (backspace internally) undoes the suggestion if the last action done was entering the suggestion with either the space bar or the candidates view. * Add an option to enable space bar autocomplete This option is off by default. Undoing a completion with backspace works in any case. * Refactor: Track selection emptyness in CurrentlyTypedWord This removes an expensive RPC call. --- srcs/juloo.keyboard2/KeyEventHandler.java | 57 +++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 7 deletions(-) (limited to 'srcs/juloo.keyboard2/KeyEventHandler.java') diff --git a/srcs/juloo.keyboard2/KeyEventHandler.java b/srcs/juloo.keyboard2/KeyEventHandler.java index 2bda20c..d078a63 100644 --- a/srcs/juloo.keyboard2/KeyEventHandler.java +++ b/srcs/juloo.keyboard2/KeyEventHandler.java @@ -30,6 +30,8 @@ public final class KeyEventHandler /** Whether to force sending arrow keys to move the cursor when [setSelection] could be used instead. */ boolean _move_cursor_force_fallback = false; + /** Whether the space bar automatically enters the best suggestion. */ + boolean _space_bar_auto_complete = false; public KeyEventHandler(IReceiver recv, Config config) { @@ -50,6 +52,8 @@ public final class KeyEventHandler _typedword.started(conf, ic); _move_cursor_force_fallback = conf.editor_config.should_move_cursor_force_fallback; + _space_bar_auto_complete = conf.space_bar_auto_complete; + clear_space_bar_state(); } /** Selection has been updated. */ @@ -123,7 +127,10 @@ public final class KeyEventHandler @Override public void suggestion_entered(String text) { - replace_text_before_cursor(_typedword.get().length(), text + " "); + String old = _typedword.get(); + replace_text_before_cursor(old.length(), text + " "); + last_replaced_word = old; + last_replacement_word_len = text.length() + 1; } @Override @@ -227,6 +234,7 @@ public final class KeyEventHandler { _autocap.event_sent(eventCode, metaState); _typedword.event_sent(eventCode, metaState); + clear_space_bar_state(); } } @@ -238,6 +246,7 @@ public final class KeyEventHandler _autocap.typed(text); _typedword.typed(text); conn.commitText(text, 1); + clear_space_bar_state(); } void replace_text_before_cursor(int remove_length, String new_text) @@ -265,9 +274,9 @@ public final class KeyEventHandler { switch (ev) { - case COPY: if(is_selection_not_empty()) send_context_menu_action(android.R.id.copy); break; + case COPY: if(_typedword.is_selection_not_empty()) send_context_menu_action(android.R.id.copy); break; case PASTE: send_context_menu_action(android.R.id.paste); break; - case CUT: if(is_selection_not_empty()) send_context_menu_action(android.R.id.cut); break; + case CUT: if(_typedword.is_selection_not_empty()) send_context_menu_action(android.R.id.cut); break; case SELECT_ALL: send_context_menu_action(android.R.id.selectAll); break; case SHARE: send_context_menu_action(android.R.id.shareText); break; case PASTE_PLAIN: send_context_menu_action(android.R.id.pasteAsPlainText); break; @@ -279,6 +288,8 @@ public final class KeyEventHandler case DELETE_WORD: send_key_down_up(KeyEvent.KEYCODE_DEL, KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON); break; case FORWARD_DELETE_WORD: send_key_down_up(KeyEvent.KEYCODE_FORWARD_DEL, KeyEvent.META_CTRL_ON | KeyEvent.META_CTRL_LEFT_ON); break; case SELECTION_CANCEL: cancel_selection(); break; + case SPACE_BAR: handle_space_bar(); break; + case BACKSPACE: handle_backspace(); break; } } @@ -497,11 +508,43 @@ public final class KeyEventHandler _recv.selection_state_changed(false); } - boolean is_selection_not_empty() + /** The word that was replaced by a suggestion when the last action was to + enter a suggestion (with the space bar or the candidates view) or [null] + otherwise. */ + String last_replaced_word = null; + /** Length of the text before the cursor that should be replaced by + backspace. */ + int last_replacement_word_len = 0; + + void handle_space_bar() { - InputConnection conn = _recv.getCurrentInputConnection(); - if (conn == null) return false; - return (conn.getSelectedText(0) != null); + if (_space_bar_auto_complete && _suggestions.best_suggestion != null + && !_typedword.is_selection_not_empty()) + { + suggestion_entered(_suggestions.best_suggestion); + } + else + { + send_text(" "); + } + } + + void handle_backspace() + { + if (last_replaced_word != null) + { + replace_text_before_cursor(last_replacement_word_len, last_replaced_word); + last_replaced_word = null; + } + else + { + send_key_down_up(KeyEvent.KEYCODE_DEL); + } + } + + void clear_space_bar_state() + { + last_replaced_word = null; } public static interface IReceiver extends Suggestions.Callback -- cgit v1.2.3