From c0b394572f35498801571ad7176eb357d5de1bf3 Mon Sep 17 00:00:00 2001 From: PouleyKetchoupp Date: Fri, 17 Jul 2020 17:44:13 +0200 Subject: [PATCH] Fix Return key events in LineEdit & TextEdit on Android Depending on the device implementation, editor actions could be received with different action ids or not at all for multi-line. Added a parameter to virtual keyboards to properly handle single-line and multi-line cases in all situations. Single-line: Input type set to text without multiline to make sure actions are sent. IME options are set to DONE action to force action id consistency. Multi-line: Input type set to text and multiline to make sure enter triggers new lines. Actions are disabled by the multiline flag, so '\n' characters are handled in text changed callbacks. --- core/bind/core_bind.cpp | 6 +++--- core/bind/core_bind.h | 2 +- core/os/os.cpp | 2 +- core/os/os.h | 2 +- doc/classes/OS.xml | 6 +++++- .../src/org/godotengine/godot/GodotIO.java | 4 ++-- .../godot/input/GodotEditText.java | 20 ++++++++++++++++--- .../godot/input/GodotTextInputWrapper.java | 4 ++-- platform/android/java_godot_io_wrapper.cpp | 6 +++--- platform/android/java_godot_io_wrapper.h | 2 +- platform/android/os_android.cpp | 4 ++-- platform/android/os_android.h | 2 +- platform/iphone/os_iphone.cpp | 2 +- platform/iphone/os_iphone.h | 2 +- platform/uwp/os_uwp.cpp | 2 +- platform/uwp/os_uwp.h | 2 +- scene/gui/line_edit.cpp | 9 +++++---- scene/gui/text_edit.cpp | 2 +- 18 files changed, 49 insertions(+), 30 deletions(-) diff --git a/core/bind/core_bind.cpp b/core/bind/core_bind.cpp index 230ce3d1b2e..c92827c2ea3 100644 --- a/core/bind/core_bind.cpp +++ b/core/bind/core_bind.cpp @@ -1068,8 +1068,8 @@ bool _OS::has_virtual_keyboard() const { return OS::get_singleton()->has_virtual_keyboard(); } -void _OS::show_virtual_keyboard(const String &p_existing_text) { - OS::get_singleton()->show_virtual_keyboard(p_existing_text, Rect2()); +void _OS::show_virtual_keyboard(const String &p_existing_text, bool p_multiline) { + OS::get_singleton()->show_virtual_keyboard(p_existing_text, Rect2(), p_multiline); } void _OS::hide_virtual_keyboard() { @@ -1372,7 +1372,7 @@ void _OS::_bind_methods() { ClassDB::bind_method(D_METHOD("dump_memory_to_file", "file"), &_OS::dump_memory_to_file); ClassDB::bind_method(D_METHOD("dump_resources_to_file", "file"), &_OS::dump_resources_to_file); ClassDB::bind_method(D_METHOD("has_virtual_keyboard"), &_OS::has_virtual_keyboard); - ClassDB::bind_method(D_METHOD("show_virtual_keyboard", "existing_text"), &_OS::show_virtual_keyboard, DEFVAL("")); + ClassDB::bind_method(D_METHOD("show_virtual_keyboard", "existing_text", "multiline"), &_OS::show_virtual_keyboard, DEFVAL(""), DEFVAL(false)); ClassDB::bind_method(D_METHOD("hide_virtual_keyboard"), &_OS::hide_virtual_keyboard); ClassDB::bind_method(D_METHOD("get_virtual_keyboard_height"), &_OS::get_virtual_keyboard_height); ClassDB::bind_method(D_METHOD("print_resources_in_use", "short"), &_OS::print_resources_in_use, DEFVAL(false)); diff --git a/core/bind/core_bind.h b/core/bind/core_bind.h index 91457a2f206..ca6ca304db4 100644 --- a/core/bind/core_bind.h +++ b/core/bind/core_bind.h @@ -256,7 +256,7 @@ public: void dump_resources_to_file(const String &p_file); bool has_virtual_keyboard() const; - void show_virtual_keyboard(const String &p_existing_text = ""); + void show_virtual_keyboard(const String &p_existing_text = "", bool p_multiline = false); void hide_virtual_keyboard(); int get_virtual_keyboard_height(); diff --git a/core/os/os.cpp b/core/os/os.cpp index 71db0e5b3a5..1ffa5c44972 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -226,7 +226,7 @@ bool OS::has_virtual_keyboard() const { return false; } -void OS::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length, int p_cursor_start, int p_cursor_end) { +void OS::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) { } void OS::hide_virtual_keyboard() { diff --git a/core/os/os.h b/core/os/os.h index c03c3229f05..160282042cf 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -388,7 +388,7 @@ public: }; virtual bool has_virtual_keyboard() const; - virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); + virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); virtual void hide_virtual_keyboard(); // returns height of the currently shown virtual keyboard (0 if keyboard is hidden) diff --git a/doc/classes/OS.xml b/doc/classes/OS.xml index b089b03b6e5..838a443cc77 100644 --- a/doc/classes/OS.xml +++ b/doc/classes/OS.xml @@ -985,8 +985,12 @@ + + - Shows the virtual keyboard if the platform has one. The [code]existing_text[/code] parameter is useful for implementing your own LineEdit, as it tells the virtual keyboard what text has already been typed (the virtual keyboard uses it for auto-correct and predictions). + Shows the virtual keyboard if the platform has one. + The [code]existing_text[/code] parameter is useful for implementing your own [LineEdit] or [TextEdit], as it tells the virtual keyboard what text has already been typed (the virtual keyboard uses it for auto-correct and predictions). + The [code]multiline[/code] parameter needs to be set to [code]true[/code] to be able to enter multiple lines of text, as in [TextEdit]. [b]Note:[/b] This method is implemented on Android, iOS and UWP. diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java index cf0766161ed..608901927df 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotIO.java @@ -493,9 +493,9 @@ public class GodotIO { return (int)(metrics.density * 160f); } - public void showKeyboard(String p_existing_text, int p_max_input_length, int p_cursor_start, int p_cursor_end) { + public void showKeyboard(String p_existing_text, boolean p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) { if (edit != null) - edit.showKeyboard(p_existing_text, p_max_input_length, p_cursor_start, p_cursor_end); + edit.showKeyboard(p_existing_text, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end); //InputMethodManager inputMgr = (InputMethodManager)activity.getSystemService(Context.INPUT_METHOD_SERVICE); //inputMgr.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java index fe2847f04f3..b76a494a586 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotEditText.java @@ -36,6 +36,7 @@ import android.content.Context; import android.os.Handler; import android.os.Message; import android.text.InputFilter; +import android.text.InputType; import android.util.AttributeSet; import android.view.KeyEvent; import android.view.inputmethod.EditorInfo; @@ -58,7 +59,8 @@ public class GodotEditText extends EditText { private GodotTextInputWrapper mInputWrapper; private EditHandler sHandler = new EditHandler(this); private String mOriginText; - private int mMaxInputLength; + private int mMaxInputLength = Integer.MAX_VALUE; + private boolean mMultiline = false; private static class EditHandler extends Handler { private final WeakReference mEdit; @@ -95,7 +97,11 @@ public class GodotEditText extends EditText { protected void initView() { this.setPadding(0, 0, 0, 0); - this.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI); + this.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_ACTION_DONE); + } + + public boolean isMultiline() { + return mMultiline; } private void handleMessage(final Message msg) { @@ -115,6 +121,12 @@ public class GodotEditText extends EditText { edit.mInputWrapper.setSelection(false); } + int inputType = InputType.TYPE_CLASS_TEXT; + if (edit.isMultiline()) { + inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE; + } + edit.setInputType(inputType); + edit.mInputWrapper.setOriginText(text); edit.addTextChangedListener(edit.mInputWrapper); final InputMethodManager imm = (InputMethodManager)mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); @@ -168,7 +180,7 @@ public class GodotEditText extends EditText { // =========================================================== // Methods // =========================================================== - public void showKeyboard(String p_existing_text, int p_max_input_length, int p_cursor_start, int p_cursor_end) { + public void showKeyboard(String p_existing_text, boolean p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) { int maxInputLength = (p_max_input_length <= 0) ? Integer.MAX_VALUE : p_max_input_length; if (p_cursor_start == -1) { // cursor position not given this.mOriginText = p_existing_text; @@ -181,6 +193,8 @@ public class GodotEditText extends EditText { this.mMaxInputLength = maxInputLength - (p_existing_text.length() - p_cursor_end); } + this.mMultiline = p_multiline; + final Message msg = new Message(); msg.what = HANDLER_OPEN_IME_KEYBOARD; msg.obj = this; diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java index ce7eb1ff37f..89a9a0709a8 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotTextInputWrapper.java @@ -123,7 +123,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene public void run() { for (int i = 0; i < count; ++i) { int key = newChars[i]; - if (key == '\n') { + if ((key == '\n') && !mEdit.isMultiline()) { // Return keys are handled through action events continue; } @@ -151,7 +151,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene }); } - if (pActionID == EditorInfo.IME_NULL) { + if (pActionID == EditorInfo.IME_ACTION_DONE) { // Enter key has been pressed GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, true); GodotLib.key(KeyEvent.KEYCODE_ENTER, 0, false); diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp index cb2c9d144bf..6b261016885 100644 --- a/platform/android/java_godot_io_wrapper.cpp +++ b/platform/android/java_godot_io_wrapper.cpp @@ -53,7 +53,7 @@ GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instanc _get_model = p_env->GetMethodID(cls, "getModel", "()Ljava/lang/String;"); _get_screen_DPI = p_env->GetMethodID(cls, "getScreenDPI", "()I"); _get_unique_id = p_env->GetMethodID(cls, "getUniqueID", "()Ljava/lang/String;"); - _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;III)V"); + _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;ZIII)V"); _hide_keyboard = p_env->GetMethodID(cls, "hideKeyboard", "()V"); _set_screen_orientation = p_env->GetMethodID(cls, "setScreenOrientation", "(I)V"); _get_system_dir = p_env->GetMethodID(cls, "getSystemDir", "(I)Ljava/lang/String;"); @@ -135,11 +135,11 @@ bool GodotIOJavaWrapper::has_vk() { return (_show_keyboard != 0) && (_hide_keyboard != 0); } -void GodotIOJavaWrapper::show_vk(const String &p_existing, int p_max_input_length, int p_cursor_start, int p_cursor_end) { +void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) { if (_show_keyboard) { JNIEnv *env = ThreadAndroid::get_env(); jstring jStr = env->NewStringUTF(p_existing.utf8().get_data()); - env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_max_input_length, p_cursor_start, p_cursor_end); + env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end); } } diff --git a/platform/android/java_godot_io_wrapper.h b/platform/android/java_godot_io_wrapper.h index 1a9eea736a1..118939915a7 100644 --- a/platform/android/java_godot_io_wrapper.h +++ b/platform/android/java_godot_io_wrapper.h @@ -73,7 +73,7 @@ public: int get_screen_dpi(); String get_unique_id(); bool has_vk(); - void show_vk(const String &p_existing, int p_max_input_length, int p_cursor_start, int p_cursor_end); + void show_vk(const String &p_existing, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end); void hide_vk(); int get_vk_height(); void set_vk_height(int p_height); diff --git a/platform/android/os_android.cpp b/platform/android/os_android.cpp index 99dd216bc2f..cd4bc01a446 100644 --- a/platform/android/os_android.cpp +++ b/platform/android/os_android.cpp @@ -558,10 +558,10 @@ int OS_Android::get_virtual_keyboard_height() const { // return 0; } -void OS_Android::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length, int p_cursor_start, int p_cursor_end) { +void OS_Android::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) { if (godot_io_java->has_vk()) { - godot_io_java->show_vk(p_existing_text, p_max_input_length, p_cursor_start, p_cursor_end); + godot_io_java->show_vk(p_existing_text, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end); } else { ERR_PRINT("Virtual keyboard not available"); diff --git a/platform/android/os_android.h b/platform/android/os_android.h index 0c957ebdb0f..cacafb70ed1 100644 --- a/platform/android/os_android.h +++ b/platform/android/os_android.h @@ -158,7 +158,7 @@ public: virtual bool has_touchscreen_ui_hint() const; virtual bool has_virtual_keyboard() const; - virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); + virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); virtual void hide_virtual_keyboard(); virtual int get_virtual_keyboard_height() const; diff --git a/platform/iphone/os_iphone.cpp b/platform/iphone/os_iphone.cpp index 1b0f71a2254..db0aab06584 100644 --- a/platform/iphone/os_iphone.cpp +++ b/platform/iphone/os_iphone.cpp @@ -482,7 +482,7 @@ extern Error _shell_open(String p_uri); extern void _set_keep_screen_on(bool p_enabled); extern void _vibrate(); -void OSIPhone::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length, int p_cursor_start, int p_cursor_end) { +void OSIPhone::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) { _show_keyboard(p_existing_text); }; diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index 179085402d5..a09e1f1fcc0 100644 --- a/platform/iphone/os_iphone.h +++ b/platform/iphone/os_iphone.h @@ -165,7 +165,7 @@ public: virtual bool can_draw() const; virtual bool has_virtual_keyboard() const; - virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); + virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); virtual void hide_virtual_keyboard(); virtual int get_virtual_keyboard_height() const; diff --git a/platform/uwp/os_uwp.cpp b/platform/uwp/os_uwp.cpp index 35838acef1b..20be1dd0d3b 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -818,7 +818,7 @@ bool OS_UWP::has_virtual_keyboard() const { return UIViewSettings::GetForCurrentView()->UserInteractionMode == UserInteractionMode::Touch; } -void OS_UWP::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, int p_max_input_length, int p_cursor_start, int p_cursor_end) { +void OS_UWP::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) { InputPane ^ pane = InputPane::GetForCurrentView(); pane->TryShow(); } diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index 1ee1439ab23..3dbe056213a 100644 --- a/platform/uwp/os_uwp.h +++ b/platform/uwp/os_uwp.h @@ -239,7 +239,7 @@ public: virtual bool has_touchscreen_ui_hint() const; virtual bool has_virtual_keyboard() const; - virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); + virtual void show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect = Rect2(), bool p_multiline = false, int p_max_input_length = -1, int p_cursor_start = -1, int p_cursor_end = -1); virtual void hide_virtual_keyboard(); virtual Error open_dynamic_library(const String p_path, void *&p_library_handle, bool p_also_set_library_path = false); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index d752979a118..497d99488ef 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -129,9 +129,9 @@ void LineEdit::_gui_input(Ref p_event) { if (OS::get_singleton()->has_virtual_keyboard()) { if (selection.enabled) { - OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length, selection.begin, selection.end); + OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), false, max_length, selection.begin, selection.end); } else { - OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length, cursor_pos); + OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), false, max_length, cursor_pos); } } } @@ -326,6 +326,7 @@ void LineEdit::_gui_input(Ref p_event) { if (OS::get_singleton()->has_virtual_keyboard()) OS::get_singleton()->hide_virtual_keyboard(); + return; } break; case KEY_BACKSPACE: { @@ -932,9 +933,9 @@ void LineEdit::_notification(int p_what) { if (OS::get_singleton()->has_virtual_keyboard()) { if (selection.enabled) { - OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length, selection.begin, selection.end); + OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), false, max_length, selection.begin, selection.end); } else { - OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length, cursor_pos); + OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), false, max_length, cursor_pos); } } } break; diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index d6e56871ab2..e818f1ee980 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -1741,7 +1741,7 @@ void TextEdit::_notification(int p_what) { OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos); if (OS::get_singleton()->has_virtual_keyboard()) - OS::get_singleton()->show_virtual_keyboard(get_text(), get_global_rect()); + OS::get_singleton()->show_virtual_keyboard(get_text(), get_global_rect(), true); } break; case NOTIFICATION_FOCUS_EXIT: {