From 41777fdda61715a59be241f0be9a8e3385222888 Mon Sep 17 00:00:00 2001 From: Jules Aguillon Date: Thu, 18 Dec 2025 19:15:32 +0100 Subject: Disable selection mode in text editors (#1141) * Disable selection mode in text editors Selection mode removes the space bar key (which is replaced by the Esc key) and can be annoying in Emacs for example. Text editor users probably have the `esc` key available. * Refactor: Move EditorInfo related code to EditorConfig Add the new EditorConfig class that handles configuration extracted from the EditorInfo. It is accessible from the Config class for convenience. This aims at reducing the length of already large classes and group the code that was spread over several classes.--- srcs/juloo.keyboard2/Autocapitalisation.java | 39 ++------- srcs/juloo.keyboard2/Config.java | 9 +- srcs/juloo.keyboard2/EditorConfig.java | 120 +++++++++++++++++++++++++++ srcs/juloo.keyboard2/KeyEventHandler.java | 20 +---- srcs/juloo.keyboard2/Keyboard2.java | 65 +++------------ srcs/juloo.keyboard2/Keyboard2View.java | 5 +- srcs/juloo.keyboard2/LayoutModifier.java | 12 +-- srcs/juloo.keyboard2/Logs.java | 5 +- 8 files changed, 157 insertions(+), 118 deletions(-) create mode 100644 srcs/juloo.keyboard2/EditorConfig.java (limited to 'srcs/juloo.keyboard2') diff --git a/srcs/juloo.keyboard2/Autocapitalisation.java b/srcs/juloo.keyboard2/Autocapitalisation.java index ec730d5..31d9a7b 100644 --- a/srcs/juloo.keyboard2/Autocapitalisation.java +++ b/srcs/juloo.keyboard2/Autocapitalisation.java @@ -2,8 +2,6 @@ package juloo.keyboard2; import android.os.Handler; import android.text.InputType; -import android.text.TextUtils; -import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.KeyEvent; @@ -22,10 +20,6 @@ public final class Autocapitalisation /** Keep track of the cursor to recognize cursor movements from typing. */ int _cursor; - static int SUPPORTED_CAPS_MODES = - InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | - InputType.TYPE_TEXT_FLAG_CAP_WORDS; - public Autocapitalisation(Handler h, Callback cb) { _handler = h; @@ -37,18 +31,19 @@ public final class Autocapitalisation * [started] does initialisation work and must be called before any other * event. */ - public void started(EditorInfo info, InputConnection ic) + public void started(Config config, InputConnection ic) { _ic = ic; - _caps_mode = info.inputType & TextUtils.CAP_MODE_SENTENCES; - if (!Config.globalConfig().autocapitalisation || _caps_mode == 0) + EditorConfig ec = config.editor_config; + if (!config.autocapitalisation || ec.caps_mode == 0) { _enabled = false; return; } _enabled = true; - _should_enable_shift = (info.initialCapsMode != 0); - _should_update_caps_mode = started_should_update_state(info.inputType); + _caps_mode = ec.caps_mode; + _should_enable_shift = ec.caps_initially_enabled; + _should_update_caps_mode = ec.caps_initially_updated; callback_now(true); } @@ -178,26 +173,4 @@ public final class Autocapitalisation return false; } } - - /** Whether the caps state should be updated when input starts. [inputType] - is the field from the editor info object. */ - boolean started_should_update_state(int inputType) - { - int class_ = inputType & InputType.TYPE_MASK_CLASS; - int variation = inputType & InputType.TYPE_MASK_VARIATION; - if (class_ != InputType.TYPE_CLASS_TEXT) - return false; - switch (variation) - { - case InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE: - case InputType.TYPE_TEXT_VARIATION_NORMAL: - case InputType.TYPE_TEXT_VARIATION_PERSON_NAME: - case InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE: - case InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT: - case InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT: - return true; - default: - return false; - } - } } diff --git a/srcs/juloo.keyboard2/Config.java b/srcs/juloo.keyboard2/Config.java index 5dbf030..577ace5 100644 --- a/srcs/juloo.keyboard2/Config.java +++ b/srcs/juloo.keyboard2/Config.java @@ -73,10 +73,9 @@ public final class Config public int clipboard_history_duration; // Dynamically set + /** Configuration options implied by the connected editor. */ + public EditorConfig editor_config; public boolean shouldOfferVoiceTyping; - public String actionLabel; // Might be 'null' - public int actionId; // Meaningful only when 'actionLabel' isn't 'null' - public boolean swapEnterActionKey; // Swap the "enter" and "action" keys public ExtraKeys extra_keys_subtype; public Map extra_keys_param; public Map extra_keys_custom; @@ -93,6 +92,7 @@ public final class Config private Config(SharedPreferences prefs, Resources res, IKeyEventHandler h, Boolean foldableUnfolded) { _prefs = prefs; + editor_config = new EditorConfig(); // static values marginTop = res.getDimension(R.dimen.margin_top); keyPadding = res.getDimension(R.dimen.key_padding); @@ -102,9 +102,6 @@ public final class Config refresh(res, foldableUnfolded); // initialized later shouldOfferVoiceTyping = false; - actionLabel = null; - actionId = 0; - swapEnterActionKey = false; extra_keys_subtype = null; handler = h; } diff --git a/srcs/juloo.keyboard2/EditorConfig.java b/srcs/juloo.keyboard2/EditorConfig.java new file mode 100644 index 0000000..d9f1ae9 --- /dev/null +++ b/srcs/juloo.keyboard2/EditorConfig.java @@ -0,0 +1,120 @@ +package juloo.keyboard2; + +import android.content.res.Resources; +import android.text.InputType; +import android.text.TextUtils; +import android.view.inputmethod.EditorInfo; + +public final class EditorConfig +{ + public String actionLabel = null; // Might be [null] + /** Action performed by the Action key. Only when [actionLabel != null]. */ + public int actionId; + public boolean swapEnterActionKey = false; // Swap the "enter" and "action" keys + /** Whether selection mode turns on automatically when text is selected. */ + public boolean selection_mode_enabled = true; + /** Whether the numeric layout should be shown by default. */ + public boolean numeric_layout = false; + /** Workaround some apps which answers to [getExtractedText] but do not react + to [setSelection] while returning [true]. */ + public boolean should_move_cursor_force_fallback = false; + + /** Autocapitalisation. */ + public int caps_mode; // Argument for [getCursorCapsMode()] + // Whether caps state is on initially. + public boolean caps_initially_enabled = false; + // Whether caps state should be updated right away. + public boolean caps_initially_updated = false; + + public EditorConfig() {} + + public void refresh(EditorInfo info, Resources res) + { + int inputType = info.inputType & InputType.TYPE_MASK_CLASS; + int options = info.imeOptions; + /* Selection mode. + Editors with [TYPE_NULL] are for example Termux and Emacs. */ + selection_mode_enabled = inputType != InputType.TYPE_NULL; + /* Action key. Looks at [info.actionLabel] first. */ + if (info.actionLabel != null) + { + actionLabel = info.actionLabel.toString(); + actionId = info.actionId; + swapEnterActionKey = false; + } + else + { + actionId = options & EditorInfo.IME_MASK_ACTION; + actionLabel = actionLabel_of_imeAction(actionId, res); + swapEnterActionKey = (options & EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0; + } + /* Numeric layout */ + switch (inputType) + { + case InputType.TYPE_CLASS_NUMBER: + case InputType.TYPE_CLASS_PHONE: + case InputType.TYPE_CLASS_DATETIME: + numeric_layout = true; + break; + default: + numeric_layout = false; + break; + } + /* setSelection fallback */ + should_move_cursor_force_fallback = _should_move_cursor_force_fallback(info); + /* Autocapitalisation */ + caps_mode = info.inputType & TextUtils.CAP_MODE_SENTENCES; + caps_initially_enabled = (info.initialCapsMode != 0); + caps_initially_updated = caps_should_update_state(info); + } + + String actionLabel_of_imeAction(int action, Resources res) + { + int id; + switch (action) + { + case EditorInfo.IME_ACTION_NEXT: id = R.string.key_action_next; break; + case EditorInfo.IME_ACTION_DONE: id = R.string.key_action_done; break; + case EditorInfo.IME_ACTION_GO: id = R.string.key_action_go; break; + case EditorInfo.IME_ACTION_PREVIOUS: id = R.string.key_action_prev; break; + case EditorInfo.IME_ACTION_SEARCH: id = R.string.key_action_search; break; + case EditorInfo.IME_ACTION_SEND: id = R.string.key_action_send; break; + case EditorInfo.IME_ACTION_UNSPECIFIED: + case EditorInfo.IME_ACTION_NONE: + default: return null; + } + return res.getString(id); + } + + boolean _should_move_cursor_force_fallback(EditorInfo info) + { + // This catch Acode: which sets several variations at once. + if ((info.inputType & InputType.TYPE_MASK_VARIATION & + InputType.TYPE_TEXT_VARIATION_PASSWORD) != 0) + return true; + // Godot editor: Doesn't handle setSelection() but returns true. + return info.packageName.startsWith("org.godotengine.editor"); + } + + /** Whether the caps state should be updated when input starts. [inputType] + is the field from the editor info object. */ + boolean caps_should_update_state(EditorInfo info) + { + int class_ = info.inputType & InputType.TYPE_MASK_CLASS; + int variation = info.inputType & InputType.TYPE_MASK_VARIATION; + if (class_ != InputType.TYPE_CLASS_TEXT) + return false; + switch (variation) + { + case InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE: + case InputType.TYPE_TEXT_VARIATION_NORMAL: + case InputType.TYPE_TEXT_VARIATION_PERSON_NAME: + case InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE: + case InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT: + case InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT: + return true; + default: + return false; + } + } +} diff --git a/srcs/juloo.keyboard2/KeyEventHandler.java b/srcs/juloo.keyboard2/KeyEventHandler.java index 33089d9..21a60c1 100644 --- a/srcs/juloo.keyboard2/KeyEventHandler.java +++ b/srcs/juloo.keyboard2/KeyEventHandler.java @@ -3,10 +3,8 @@ package juloo.keyboard2; import android.annotation.SuppressLint; import android.os.Looper; import android.os.Handler; -import android.text.InputType; import android.view.KeyCharacterMap; import android.view.KeyEvent; -import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; @@ -38,10 +36,11 @@ public final class KeyEventHandler } /** Editing just started. */ - public void started(EditorInfo info) + public void started(Config conf) { - _autocap.started(info, _recv.getCurrentInputConnection()); - _move_cursor_force_fallback = should_move_cursor_force_fallback(info); + _autocap.started(conf, _recv.getCurrentInputConnection()); + _move_cursor_force_fallback = + conf.editor_config.should_move_cursor_force_fallback; } /** Selection has been updated. */ @@ -468,17 +467,6 @@ public final class KeyEventHandler return (conn.getSelectedText(0) != null); } - /** Workaround some apps which answers to [getExtractedText] but do not react - to [setSelection] while returning [true]. */ - boolean should_move_cursor_force_fallback(EditorInfo info) - { - // This catch Acode: which sets several variations at once. - if ((info.inputType & InputType.TYPE_MASK_VARIATION & InputType.TYPE_TEXT_VARIATION_PASSWORD) != 0) - return true; - // Godot editor: Doesn't handle setSelection() but returns true. - return info.packageName.startsWith("org.godotengine.editor"); - } - public static interface IReceiver { public void handle_event_key(KeyValue.Event ev); diff --git a/srcs/juloo.keyboard2/Keyboard2.java b/srcs/juloo.keyboard2/Keyboard2.java index 0297cc4..f0408e0 100644 --- a/srcs/juloo.keyboard2/Keyboard2.java +++ b/srcs/juloo.keyboard2/Keyboard2.java @@ -38,7 +38,6 @@ public class Keyboard2 extends InputMethodService private KeyboardData _localeTextLayout; private ViewGroup _emojiPane = null; private ViewGroup _clipboard_pane = null; - public int actionId; // Action performed by the Action key. private Handler _handler; private Config _config; @@ -202,44 +201,6 @@ public class Keyboard2 extends InputMethodService _localeTextLayout = default_layout; } - private String actionLabel_of_imeAction(int action) - { - int res; - switch (action) - { - case EditorInfo.IME_ACTION_NEXT: res = R.string.key_action_next; break; - case EditorInfo.IME_ACTION_DONE: res = R.string.key_action_done; break; - case EditorInfo.IME_ACTION_GO: res = R.string.key_action_go; break; - case EditorInfo.IME_ACTION_PREVIOUS: res = R.string.key_action_prev; break; - case EditorInfo.IME_ACTION_SEARCH: res = R.string.key_action_search; break; - case EditorInfo.IME_ACTION_SEND: res = R.string.key_action_send; break; - case EditorInfo.IME_ACTION_UNSPECIFIED: - case EditorInfo.IME_ACTION_NONE: - default: return null; - } - return getResources().getString(res); - } - - private void refresh_action_label(EditorInfo info) - { - // First try to look at 'info.actionLabel', if it isn't set, look at - // 'imeOptions'. - if (info.actionLabel != null) - { - _config.actionLabel = info.actionLabel.toString(); - actionId = info.actionId; - _config.swapEnterActionKey = false; - } - else - { - int action = info.imeOptions & EditorInfo.IME_MASK_ACTION; - _config.actionLabel = actionLabel_of_imeAction(action); // Might be null - actionId = action; - _config.swapEnterActionKey = - (info.imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) == 0; - } - } - /** Might re-create the keyboard view. [_keyboardView.setKeyboard()] and [setInputView()] must be called soon after. */ private void refresh_config() @@ -258,19 +219,15 @@ public class Keyboard2 extends InputMethodService _keyboardView.reset(); } - private KeyboardData refresh_special_layout(EditorInfo info) + private KeyboardData refresh_special_layout() { - switch (info.inputType & InputType.TYPE_MASK_CLASS) + if (_config.editor_config.numeric_layout) { - case InputType.TYPE_CLASS_NUMBER: - case InputType.TYPE_CLASS_PHONE: - case InputType.TYPE_CLASS_DATETIME: - if (_config.selected_number_layout == NumberLayout.PIN) - return loadPinentry(R.xml.pin); - else if (_config.selected_number_layout == NumberLayout.NUMBER) - return loadNumpad(R.xml.numeric); - default: - break; + switch (_config.selected_number_layout) + { + case PIN: return loadPinentry(R.xml.pin); + case NUMBER: return loadNumpad(R.xml.numeric); + } } return null; } @@ -278,11 +235,11 @@ public class Keyboard2 extends InputMethodService @Override public void onStartInputView(EditorInfo info, boolean restarting) { + _config.editor_config.refresh(info, getResources()); refresh_config(); - refresh_action_label(info); - _currentSpecialLayout = refresh_special_layout(info); + _currentSpecialLayout = refresh_special_layout(); _keyboardView.setKeyboard(current_layout()); - _keyeventhandler.started(info); + _keyeventhandler.started(_config); setInputView(_keyboardView); Logs.debug_startup_input_view(info, _config); } @@ -453,7 +410,7 @@ public class Keyboard2 extends InputMethodService case ACTION: InputConnection conn = getCurrentInputConnection(); if (conn != null) - conn.performEditorAction(actionId); + conn.performEditorAction(_config.editor_config.actionId); break; case SWITCH_FORWARD: diff --git a/srcs/juloo.keyboard2/Keyboard2View.java b/srcs/juloo.keyboard2/Keyboard2View.java index 01afe91..1532970 100644 --- a/srcs/juloo.keyboard2/Keyboard2View.java +++ b/srcs/juloo.keyboard2/Keyboard2View.java @@ -147,8 +147,9 @@ public class Keyboard2View extends View /** Called from [Keybard2.onUpdateSelection]. */ public void set_selection_state(boolean selection_state) { - set_fake_ptr_latched(KeyboardData.Key.EMPTY, - KeyValue.getKeyByName("selection_mode"), selection_state, true); + if (_config.editor_config.selection_mode_enabled) + set_fake_ptr_latched(KeyboardData.Key.EMPTY, + KeyValue.getKeyByName("selection_mode"), selection_state, true); } public KeyValue modifyKey(KeyValue k, Pointers.Modifiers mods) diff --git a/srcs/juloo.keyboard2/LayoutModifier.java b/srcs/juloo.keyboard2/LayoutModifier.java index 22f15ec..fa7be0c 100644 --- a/srcs/juloo.keyboard2/LayoutModifier.java +++ b/srcs/juloo.keyboard2/LayoutModifier.java @@ -149,6 +149,7 @@ public final class LayoutModifier */ static KeyValue modify_key(KeyValue orig) { + EditorConfig ec = globalConfig.editor_config; switch (orig.getKind()) { case Event: @@ -159,11 +160,12 @@ public final class LayoutModifier return KeyValue.getKeyByName("change_method_prev"); break; case ACTION: - if (globalConfig.actionLabel == null) + String action_label = ec.actionLabel; + if (action_label == null) return null; // Remove the action key - if (globalConfig.swapEnterActionKey) + if (ec.swapEnterActionKey) return KeyValue.getKeyByName("enter"); - return KeyValue.makeActionKey(globalConfig.actionLabel); + return KeyValue.makeActionKey(action_label); case SWITCH_FORWARD: return (globalConfig.layouts.size() > 1) ? orig : null; case SWITCH_BACKWARD: @@ -177,8 +179,8 @@ public final class LayoutModifier switch (orig.getKeyevent()) { case KeyEvent.KEYCODE_ENTER: - if (globalConfig.swapEnterActionKey && globalConfig.actionLabel != null) - return KeyValue.makeActionKey(globalConfig.actionLabel); + if (ec.swapEnterActionKey && ec.actionLabel != null) + return KeyValue.makeActionKey(ec.actionLabel); break; } break; diff --git a/srcs/juloo.keyboard2/Logs.java b/srcs/juloo.keyboard2/Logs.java index 8843052..1bef51c 100644 --- a/srcs/juloo.keyboard2/Logs.java +++ b/srcs/juloo.keyboard2/Logs.java @@ -23,8 +23,9 @@ public final class Logs info.dump(_debug_logs, ""); if (info.extras != null) _debug_logs.println("extras: "+info.extras.toString()); - _debug_logs.println("swapEnterActionKey: "+conf.swapEnterActionKey); - _debug_logs.println("actionLabel: "+conf.actionLabel); + _debug_logs.println("swapEnterActionKey: " + +conf.editor_config.swapEnterActionKey); + _debug_logs.println("actionLabel: "+conf.editor_config.actionLabel); } public static void debug_config_migration(int from_version, int to_version) -- cgit v1.2.3