From 2197ef05662913d81df5846516930c3c47739001 Mon Sep 17 00:00:00 2001 From: SkyJJ Date: Tue, 28 Apr 2020 19:57:45 +0200 Subject: [PATCH] Fix Android LineEdit editing bugs (cherry picked from commit cc473b948f814a52b383590791b5226d15a8b010) --- core/os/os.cpp | 2 +- core/os/os.h | 2 +- .../src/org/godotengine/godot/GodotIO.java | 4 +- .../godot/input/GodotEditText.java | 39 +++++++++++++------ .../godot/input/GodotTextInputWrapper.java | 10 +++++ 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 | 3 +- platform/uwp/os_uwp.h | 2 +- scene/gui/line_edit.cpp | 27 +++++++++---- 14 files changed, 71 insertions(+), 36 deletions(-) diff --git a/core/os/os.cpp b/core/os/os.cpp index 1ed94842081..be1c93ea007 100644 --- a/core/os/os.cpp +++ b/core/os/os.cpp @@ -221,7 +221,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) { +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::hide_virtual_keyboard() { diff --git a/core/os/os.h b/core/os/os.h index f355cc09e51..56180216cbf 100644 --- a/core/os/os.h +++ b/core/os/os.h @@ -377,7 +377,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); + 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 hide_virtual_keyboard(); // returns height of the currently shown virtual keyboard (0 if keyboard is hidden) 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 9efef2bcbf4..cf0766161ed 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) { + public void showKeyboard(String p_existing_text, 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); + edit.showKeyboard(p_existing_text, 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 c35ecb82a29..fe2847f04f3 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 @@ -58,6 +58,7 @@ public class GodotEditText extends EditText { private GodotTextInputWrapper mInputWrapper; private EditHandler sHandler = new EditHandler(this); private String mOriginText; + private int mMaxInputLength; private static class EditHandler extends Handler { private final WeakReference mEdit; @@ -104,11 +105,18 @@ public class GodotEditText extends EditText { String text = edit.mOriginText; if (edit.requestFocus()) { edit.removeTextChangedListener(edit.mInputWrapper); + setMaxInputLength(edit); edit.setText(""); edit.append(text); + if (msg.arg2 != -1) { + edit.setSelection(msg.arg1, msg.arg2); + edit.mInputWrapper.setSelection(true); + } else { + edit.mInputWrapper.setSelection(false); + } + edit.mInputWrapper.setOriginText(text); edit.addTextChangedListener(edit.mInputWrapper); - setMaxInputLength(edit, msg.arg1); final InputMethodManager imm = (InputMethodManager)mView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showSoftInput(edit, 0); } @@ -125,14 +133,10 @@ public class GodotEditText extends EditText { } } - private void setMaxInputLength(EditText p_edit_text, int p_max_input_length) { - if (p_max_input_length > 0) { - InputFilter[] filters = new InputFilter[1]; - filters[0] = new InputFilter.LengthFilter(p_max_input_length); - p_edit_text.setFilters(filters); - } else { - p_edit_text.setFilters(new InputFilter[] {}); - } + private void setMaxInputLength(EditText p_edit_text) { + InputFilter[] filters = new InputFilter[1]; + filters[0] = new InputFilter.LengthFilter(this.mMaxInputLength); + p_edit_text.setFilters(filters); } // =========================================================== @@ -164,13 +168,24 @@ public class GodotEditText extends EditText { // =========================================================== // Methods // =========================================================== - public void showKeyboard(String p_existing_text, int p_max_input_length) { - this.mOriginText = p_existing_text; + public void showKeyboard(String p_existing_text, 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; + this.mMaxInputLength = maxInputLength; + } else if (p_cursor_end == -1) { // not text selection + this.mOriginText = p_existing_text.substring(0, p_cursor_start); + this.mMaxInputLength = maxInputLength - (p_existing_text.length() - p_cursor_start); + } else { + this.mOriginText = p_existing_text.substring(0, p_cursor_end); + this.mMaxInputLength = maxInputLength - (p_existing_text.length() - p_cursor_end); + } final Message msg = new Message(); msg.what = HANDLER_OPEN_IME_KEYBOARD; msg.obj = this; - msg.arg1 = p_max_input_length; + msg.arg1 = p_cursor_start; + msg.arg2 = p_cursor_end; sHandler.sendMessage(msg); } 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 944b2dbc188..ce7eb1ff37f 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 @@ -53,6 +53,7 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene private final GodotView mView; private final GodotEditText mEdit; private String mOriginText; + private boolean mHasSelection; // =========================================================== // Constructors @@ -77,6 +78,10 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene this.mOriginText = originText; } + public void setSelection(boolean selection) { + mHasSelection = selection; + } + // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== @@ -95,6 +100,11 @@ public class GodotTextInputWrapper implements TextWatcher, OnEditorActionListene for (int i = 0; i < count; ++i) { GodotLib.key(KeyEvent.KEYCODE_DEL, 0, true); GodotLib.key(KeyEvent.KEYCODE_DEL, 0, false); + + if (mHasSelection) { + mHasSelection = false; + break; + } } } }); diff --git a/platform/android/java_godot_io_wrapper.cpp b/platform/android/java_godot_io_wrapper.cpp index 8d075f8e972..cb2c9d144bf 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;I)V"); + _show_keyboard = p_env->GetMethodID(cls, "showKeyboard", "(Ljava/lang/String;III)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) { +void GodotIOJavaWrapper::show_vk(const String &p_existing, 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); + env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, 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 7dfed521876..1a9eea736a1 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); + void show_vk(const String &p_existing, 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 e5d9bdc60c1..99dd216bc2f 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) { +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) { if (godot_io_java->has_vk()) { - godot_io_java->show_vk(p_existing_text, p_max_input_length); + godot_io_java->show_vk(p_existing_text, 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 c2f9a0992f3..0c957ebdb0f 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); + 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 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 7a699f9b50e..1b0f71a2254 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) { +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) { _show_keyboard(p_existing_text); }; diff --git a/platform/iphone/os_iphone.h b/platform/iphone/os_iphone.h index d2d96181f55..179085402d5 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); + 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 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 d5047b53ab1..90c1697e1c5 100644 --- a/platform/uwp/os_uwp.cpp +++ b/platform/uwp/os_uwp.cpp @@ -801,8 +801,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) { - +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) { InputPane ^ pane = InputPane::GetForCurrentView(); pane->TryShow(); } diff --git a/platform/uwp/os_uwp.h b/platform/uwp/os_uwp.h index fb43ab382e8..1ee1439ab23 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); + 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 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 e659641ebda..f255b643f0b 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -127,8 +127,13 @@ void LineEdit::_gui_input(Ref p_event) { selection.creating = false; selection.doubleclick = false; - if (OS::get_singleton()->has_virtual_keyboard()) - OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length); + 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); + } else { + OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length, cursor_pos); + } + } } update(); @@ -903,13 +908,19 @@ void LineEdit::_notification(int p_what) { draw_caret = true; } - OS::get_singleton()->set_ime_active(true); - Point2 cursor_pos = Point2(get_cursor_position(), 1) * get_minimum_size().height; - OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos); - - if (OS::get_singleton()->has_virtual_keyboard()) - OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length); + { + OS::get_singleton()->set_ime_active(true); + Point2 cursor_pos2 = Point2(get_cursor_position(), 1) * get_minimum_size().height; + OS::get_singleton()->set_ime_position(get_global_position() + cursor_pos2); + } + 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); + } else { + OS::get_singleton()->show_virtual_keyboard(text, get_global_rect(), max_length, cursor_pos); + } + } } break; case NOTIFICATION_FOCUS_EXIT: {