diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index a4ccb13ed48..3042771b64b 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -1204,6 +1204,14 @@ [b]Note:[/b] On iOS, this method has no effect if [member ProjectSettings.display/window/handheld/orientation] is not set to [constant SCREEN_SENSOR]. + + + + + Sets the [param callable] that should be called when hardware keyboard is connected/disconnected. [param callable] should accept a single [bool] parameter indicating whether the keyboard is connected (true) or disconnected (false). + [b]Note:[/b] This method is only implemented on Android. + + diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml index cb910eb8467..5a765d56f8e 100644 --- a/doc/classes/EditorSettings.xml +++ b/doc/classes/EditorSettings.xml @@ -1102,6 +1102,10 @@ If [code]true[/code], enable two finger pan and scale gestures on touchscreen devices. [b]Note:[/b] Defaults to [code]true[/code] on touchscreen devices. + + If [code]true[/code], enables the TouchActionsPanel to provide easy access to keyboard shortcuts on touchscreen devices. + [b]Note:[/b] Only available in the Android editor. + If [code]true[/code], increases the scrollbar touch area to improve usability on touchscreen devices. [b]Note:[/b] Defaults to [code]true[/code] on touchscreen devices. diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index ba16a6c1fb5..b0d21c58dc5 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -7955,7 +7955,10 @@ EditorNode::EditorNode() { #ifdef ANDROID_ENABLED // Add TouchActionsPanel node. - add_child(memnew(TouchActionsPanel)); + bool is_enabled = EDITOR_GET("interface/touchscreen/enable_touch_actions_panel"); + if (is_enabled) { + add_child(memnew(TouchActionsPanel)); + } #endif // Bottom panels. diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index 5985ff342e3..0a293144001 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -584,6 +584,10 @@ void EditorSettings::_load_defaults(Ref p_extra_config) { EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "interface/touchscreen/scale_gizmo_handles", has_touchscreen_ui ? 3 : 1, "1,5,1") set_restart_if_changed("interface/touchscreen/scale_gizmo_handles", true); + // Only available in the Android editor. + EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_touch_actions_panel", true, "") + set_restart_if_changed("interface/touchscreen/enable_touch_actions_panel", true); + // Disable some touchscreen settings by default for the XR Editor. bool is_native_touchscreen = has_touchscreen_ui && !OS::get_singleton()->has_feature("xr_editor"); EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/enable_long_press_as_right_click", is_native_touchscreen, "") diff --git a/editor/gui/touch_actions_panel.cpp b/editor/gui/touch_actions_panel.cpp index 29a0143899e..ba61bb3e16d 100644 --- a/editor/gui/touch_actions_panel.cpp +++ b/editor/gui/touch_actions_panel.cpp @@ -41,6 +41,10 @@ void TouchActionsPanel::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + DisplayServer::get_singleton()->set_hardware_keyboard_connection_change_callback(callable_mp(this, &TouchActionsPanel::_hardware_keyboard_connected)); + _hardware_keyboard_connected(DisplayServer::get_singleton()->has_hardware_keyboard()); + } break; case NOTIFICATION_THEME_CHANGED: { drag_handle->set_texture(get_editor_theme_icon(SNAME("DragHandle"))); layout_toggle_button->set_button_icon(get_editor_theme_icon(SNAME("Orientation"))); @@ -53,6 +57,10 @@ void TouchActionsPanel::_notification(int p_what) { } } +void TouchActionsPanel::_hardware_keyboard_connected(bool p_connected) { + set_visible(!p_connected); +} + void TouchActionsPanel::_simulate_editor_shortcut(const String &p_shortcut_name) { Ref shortcut = ED_GET_SHORTCUT(p_shortcut_name); diff --git a/editor/gui/touch_actions_panel.h b/editor/gui/touch_actions_panel.h index 516d37d2820..ddb637b1dc3 100644 --- a/editor/gui/touch_actions_panel.h +++ b/editor/gui/touch_actions_panel.h @@ -63,6 +63,8 @@ private: void _lock_panel_toggled(bool p_pressed); Button *_add_new_action_button(const String &p_shortcut, Key p_keycode = Key::NONE); + void _hardware_keyboard_connected(bool p_connected); + public: TouchActionsPanel(); }; diff --git a/platform/android/display_server_android.cpp b/platform/android/display_server_android.cpp index 5db5d85aa6b..b174b178553 100644 --- a/platform/android/display_server_android.cpp +++ b/platform/android/display_server_android.cpp @@ -145,6 +145,16 @@ void DisplayServerAndroid::emit_system_theme_changed() { } } +void DisplayServerAndroid::set_hardware_keyboard_connection_change_callback(const Callable &p_callable) { + hardware_keyboard_connection_changed = p_callable; +} + +void DisplayServerAndroid::emit_hardware_keyboard_connection_changed(bool p_connected) { + if (hardware_keyboard_connection_changed.is_valid()) { + hardware_keyboard_connection_changed.call_deferred(p_connected); + } +} + void DisplayServerAndroid::clipboard_set(const String &p_text) { GodotJavaWrapper *godot_java = OS_Android::get_singleton()->get_godot_java(); ERR_FAIL_NULL(godot_java); diff --git a/platform/android/display_server_android.h b/platform/android/display_server_android.h index 894ef311fa1..fb1212141cc 100644 --- a/platform/android/display_server_android.h +++ b/platform/android/display_server_android.h @@ -89,6 +89,7 @@ class DisplayServerAndroid : public DisplayServer { Callable rect_changed_callback; Callable system_theme_changed; + Callable hardware_keyboard_connection_changed; Callable dialog_callback; Callable input_dialog_callback; @@ -114,11 +115,10 @@ public: virtual void tts_resume() override; virtual void tts_stop() override; - void emit_system_theme_changed(); - virtual bool is_dark_mode_supported() const override; virtual bool is_dark_mode() const override; virtual void set_system_theme_change_callback(const Callable &p_callable) override; + void emit_system_theme_changed(); virtual void clipboard_set(const String &p_text) override; virtual String clipboard_get() const override; @@ -159,6 +159,8 @@ public: virtual void virtual_keyboard_hide() override; virtual int virtual_keyboard_get_height() const override; virtual bool has_hardware_keyboard() const override; + virtual void set_hardware_keyboard_connection_change_callback(const Callable &p_callable) override; + void emit_hardware_keyboard_connection_changed(bool p_connected); virtual void window_set_window_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override; virtual void window_set_input_event_callback(const Callable &p_callable, WindowID p_window = MAIN_WINDOW_ID) override; diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index 9ab30e5f5b6..6a5c5eb260e 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -77,7 +77,6 @@ import org.godotengine.godot.xr.XRMode import java.io.File import java.io.FileInputStream import java.io.InputStream -import java.lang.Exception import java.security.MessageDigest import java.util.* import java.util.concurrent.atomic.AtomicBoolean diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java index d70915a9b37..5e599432d91 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotLib.java @@ -268,6 +268,11 @@ public class GodotLib { */ public static native void onNightModeChanged(); + /** + * Invoked on the hardware keyboard connected/disconnected. + */ + public static native void hardwareKeyboardConnected(boolean connected); + /** * Invoked on the file picker closed. */ 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 cacc1643e30..68f5ffd37cf 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 @@ -265,13 +265,6 @@ public class GodotEditText extends EditText { } public boolean hasHardwareKeyboard() { - Configuration config = getResources().getConfiguration(); - boolean hasHardwareKeyboardConfig = config.keyboard != Configuration.KEYBOARD_NOKEYS && - config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; - if (hasHardwareKeyboardConfig) { - return true; - } - return mRenderView.getInputHandler().hasHardwareKeyboard(); } diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java index 695d56c24f8..205aae3347a 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java +++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java @@ -90,6 +90,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens private int cachedRotation = -1; private boolean overrideVolumeButtons = false; + private boolean hasHardwareKeyboardConfig = false; public GodotInputHandler(Context context, Godot godot) { this.godot = godot; @@ -105,6 +106,9 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { this.scaleGestureDetector.setStylusScaleEnabled(true); } + Configuration config = context.getResources().getConfiguration(); + hasHardwareKeyboardConfig = config.keyboard != Configuration.KEYBOARD_NOKEYS && + config.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; } /** @@ -143,6 +147,9 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens } boolean hasHardwareKeyboard() { + if (hasHardwareKeyboardConfig) { + return true; + } return !mHardwareKeyboardIds.isEmpty(); } @@ -797,5 +804,12 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens public void onConfigurationChanged(Configuration newConfig) { updateCachedRotation(); + + boolean newHardwareKeyboardConfig = newConfig.keyboard != Configuration.KEYBOARD_NOKEYS && + newConfig.hardKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO; + if (hasHardwareKeyboardConfig != newHardwareKeyboardConfig) { + hasHardwareKeyboardConfig = newHardwareKeyboardConfig; + GodotLib.hardwareKeyboardConnected(hasHardwareKeyboard()); + } } } diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp index 196dfd88f46..066cf86d1c2 100644 --- a/platform/android/java_godot_lib_jni.cpp +++ b/platform/android/java_godot_lib_jni.cpp @@ -548,6 +548,13 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JN } } +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hardwareKeyboardConnected(JNIEnv *env, jclass clazz, jboolean p_connected) { + DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton(); + if (ds) { + ds->emit_hardware_keyboard_connection_changed(p_connected); + } +} + JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths) { DisplayServerAndroid *ds = (DisplayServerAndroid *)DisplayServer::get_singleton(); if (ds) { diff --git a/platform/android/java_godot_lib_jni.h b/platform/android/java_godot_lib_jni.h index 47a5a62fb93..7cdc22d7189 100644 --- a/platform/android/java_godot_lib_jni.h +++ b/platform/android/java_godot_lib_jni.h @@ -68,6 +68,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setEditorProjectMetad JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setVirtualKeyboardHeight(JNIEnv *env, jclass clazz, jint p_height); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_requestPermissionResult(JNIEnv *env, jclass clazz, jstring p_permission, jboolean p_result); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onNightModeChanged(JNIEnv *env, jclass clazz); +JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_hardwareKeyboardConnected(JNIEnv *env, jclass clazz, jboolean p_connected); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_filePickerCallback(JNIEnv *env, jclass clazz, jboolean p_ok, jobjectArray p_selected_paths); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererResumed(JNIEnv *env, jclass clazz); JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_onRendererPaused(JNIEnv *env, jclass clazz); diff --git a/servers/display_server.cpp b/servers/display_server.cpp index 450ffd2120e..32fdc36cbe4 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -1033,6 +1033,7 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("virtual_keyboard_get_height"), &DisplayServer::virtual_keyboard_get_height); ClassDB::bind_method(D_METHOD("has_hardware_keyboard"), &DisplayServer::has_hardware_keyboard); + ClassDB::bind_method(D_METHOD("set_hardware_keyboard_connection_change_callback", "callable"), &DisplayServer::set_hardware_keyboard_connection_change_callback); ClassDB::bind_method(D_METHOD("cursor_set_shape", "shape"), &DisplayServer::cursor_set_shape); ClassDB::bind_method(D_METHOD("cursor_get_shape"), &DisplayServer::cursor_get_shape); diff --git a/servers/display_server.h b/servers/display_server.h index 075c19ae548..345d0752bdb 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -272,6 +272,7 @@ public: virtual Color get_accent_color() const { return Color(0, 0, 0, 0); } virtual Color get_base_color() const { return Color(0, 0, 0, 0); } virtual void set_system_theme_change_callback(const Callable &p_callable) {} + virtual void set_hardware_keyboard_connection_change_callback(const Callable &p_callable) {} private: static bool window_early_clear_override_enabled;