diff --git a/doc/classes/EditorSettings.xml b/doc/classes/EditorSettings.xml
index 9c8e03e04d5..d0cf8017a80 100644
--- a/doc/classes/EditorSettings.xml
+++ b/doc/classes/EditorSettings.xml
@@ -1105,10 +1105,6 @@
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.
@@ -1117,6 +1113,10 @@
Specify the multiplier to apply to the scale for the editor gizmo handles to improve usability on touchscreen devices.
[b]Note:[/b] Defaults to [code]1[/code] on non-touchscreen devices.
+
+ A touch-friendly panel that provides easy access to common actions such as save, delete, undo, and redo without requiring a keyboard.
+ [b]Note:[/b] Only available in the Android and XR editor.
+
Specifies how the engine should check for updates.
- [b]Disable Update Checks[/b] will block the engine from checking updates (see also [member network/connection/network_mode]).
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 05af591d84b..d4839bb3706 100644
--- a/editor/editor_node.cpp
+++ b/editor/editor_node.cpp
@@ -940,6 +940,11 @@ void EditorNode::_notification(int p_what) {
if (EditorSettings::get_singleton()->check_changed_settings_in_group("text_editor/theme/highlighting")) {
EditorHelpHighlighter::get_singleton()->reset_cache();
}
+#endif
+#ifdef ANDROID_ENABLED
+ if (EditorSettings::get_singleton()->check_changed_settings_in_group("interface/touchscreen/touch_actions_panel")) {
+ _touch_actions_panel_mode_changed();
+ }
#endif
} break;
}
@@ -7063,6 +7068,34 @@ void EditorNode::set_unfocused_low_processor_usage_mode_enabled(bool p_enabled)
unfocused_low_processor_usage_mode_enabled = p_enabled;
}
+#ifdef ANDROID_ENABLED
+void EditorNode::_touch_actions_panel_mode_changed() {
+ int panel_mode = EDITOR_GET("interface/touchscreen/touch_actions_panel");
+ switch (panel_mode) {
+ case 1:
+ if (touch_actions_panel != nullptr) {
+ touch_actions_panel->queue_free();
+ }
+ touch_actions_panel = memnew(TouchActionsPanel);
+ main_hbox->call_deferred("add_child", touch_actions_panel);
+ break;
+ case 2:
+ if (touch_actions_panel != nullptr) {
+ touch_actions_panel->queue_free();
+ }
+ touch_actions_panel = memnew(TouchActionsPanel);
+ call_deferred("add_child", touch_actions_panel);
+ break;
+ case 0:
+ if (touch_actions_panel != nullptr) {
+ touch_actions_panel->queue_free();
+ touch_actions_panel = nullptr;
+ }
+ break;
+ }
+}
+#endif
+
EditorNode::EditorNode() {
DEV_ASSERT(!singleton);
singleton = this;
@@ -7368,7 +7401,20 @@ EditorNode::EditorNode() {
gui_base->set_end(Point2(0, 0));
main_vbox = memnew(VBoxContainer);
+
+#ifdef ANDROID_ENABLED
+ main_hbox = memnew(HBoxContainer);
+ main_hbox->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT);
+
+ main_hbox->add_child(main_vbox);
+ main_vbox->set_h_size_flags(Control::SIZE_EXPAND_FILL);
+
+ _touch_actions_panel_mode_changed();
+
+ gui_base->add_child(main_hbox);
+#else
gui_base->add_child(main_vbox);
+#endif
title_bar = memnew(EditorTitleBar);
main_vbox->add_child(title_bar);
@@ -7952,14 +7998,6 @@ EditorNode::EditorNode() {
_update_layouts_menu();
-#ifdef ANDROID_ENABLED
- // Add TouchActionsPanel node.
- bool is_enabled = EDITOR_GET("interface/touchscreen/enable_touch_actions_panel");
- if (is_enabled) {
- add_child(memnew(TouchActionsPanel));
- }
-#endif
-
// Bottom panels.
bottom_panel = memnew(EditorBottomPanel);
diff --git a/editor/editor_node.h b/editor/editor_node.h
index bcd21bb9621..895a8d5d0c1 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -99,6 +99,11 @@ class ProjectSettingsEditor;
class SceneImportSettingsDialog;
class ProjectUpgradeTool;
+#ifdef ANDROID_ENABLED
+class HBoxContainer;
+class TouchActionsPanel;
+#endif
+
struct EditorProgress {
String task;
bool force_background = false;
@@ -276,6 +281,12 @@ private:
VBoxContainer *main_vbox = nullptr;
OptionButton *renderer = nullptr;
+#ifdef ANDROID_ENABLED
+ HBoxContainer *main_hbox = nullptr; // Only created on Android for TouchActionsPanel.
+ TouchActionsPanel *touch_actions_panel = nullptr;
+ void _touch_actions_panel_mode_changed();
+#endif
+
ConfirmationDialog *video_restart_dialog = nullptr;
int renderer_current = 0;
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index 76b37a37f72..82d96c014d8 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -584,10 +584,6 @@ 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, "")
@@ -595,6 +591,10 @@ void EditorSettings::_load_defaults(Ref p_extra_config) {
EDITOR_SETTING(Variant::BOOL, PROPERTY_HINT_NONE, "interface/touchscreen/increase_scrollbar_touch_area", is_native_touchscreen, "")
set_restart_if_changed("interface/touchscreen/increase_scrollbar_touch_area", true);
+ // Only available in the Android editor.
+ String touch_actions_panel_hints = "Disabled:0,Embedded Panel:1,Floating Panel:2";
+ EDITOR_SETTING_BASIC(Variant::INT, PROPERTY_HINT_ENUM, "interface/touchscreen/touch_actions_panel", 1, touch_actions_panel_hints)
+
// Scene tabs
EDITOR_SETTING(Variant::INT, PROPERTY_HINT_ENUM, "interface/scene_tabs/display_close_button", 1, "Never,If Tab Active,Always"); // TabBar::CloseButtonDisplayPolicy
_initial_set("interface/scene_tabs/show_thumbnail_on_hover", true);
diff --git a/editor/gui/touch_actions_panel.cpp b/editor/gui/touch_actions_panel.cpp
index 5e6822adb19..bec5a25a287 100644
--- a/editor/gui/touch_actions_panel.cpp
+++ b/editor/gui/touch_actions_panel.cpp
@@ -44,14 +44,25 @@ void TouchActionsPanel::_notification(int 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());
+ if (!is_floating) {
+ get_parent()->move_child(this, embedded_panel_index);
+ }
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
set_process_input(is_visible_in_tree());
} 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")));
- lock_panel_button->set_button_icon(get_editor_theme_icon(SNAME("Lock")));
+ if (is_floating) {
+ drag_handle->set_texture(get_editor_theme_icon(SNAME("DragHandle")));
+ layout_toggle_button->set_button_icon(get_editor_theme_icon(SNAME("Orientation")));
+ lock_panel_button->set_button_icon(get_editor_theme_icon(SNAME("Lock")));
+ } else {
+ if (embedded_panel_index == 1) {
+ panel_pos_button->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignLeftWide")));
+ } else {
+ panel_pos_button->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignRightWide")));
+ }
+ }
save_button->set_button_icon(get_editor_theme_icon(SNAME("Save")));
delete_button->set_button_icon(get_editor_theme_icon(SNAME("Remove")));
undo_button->set_button_icon(get_editor_theme_icon(SNAME("UndoRedo")));
@@ -154,7 +165,7 @@ void TouchActionsPanel::_add_new_modifier_button(Modifier p_modifier) {
}
void TouchActionsPanel::_on_drag_handle_gui_input(const Ref &p_event) {
- if (lock_panel_position) {
+ if (locked_panel) {
return;
}
Ref mouse_button_event = p_event;
@@ -191,50 +202,87 @@ void TouchActionsPanel::_switch_layout() {
}
void TouchActionsPanel::_lock_panel_toggled(bool p_pressed) {
- lock_panel_position = p_pressed;
- layout_toggle_button->set_disabled(p_pressed);
+ locked_panel = p_pressed;
+ layout_toggle_button->set_visible(!p_pressed);
+ drag_handle->set_visible(!p_pressed);
+ reset_size();
+ queue_redraw();
+}
+
+void TouchActionsPanel::_switch_embedded_panel_side() {
+ if (embedded_panel_index == 0) {
+ embedded_panel_index = 1;
+ panel_pos_button->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignLeftWide")));
+ } else {
+ embedded_panel_index = 0;
+ panel_pos_button->set_button_icon(get_editor_theme_icon(SNAME("ControlAlignRightWide")));
+ }
+ get_parent()->move_child(this, embedded_panel_index); // Parent is a hbox with only two children -- TouchActionsPanel and main Editor UI.
+ EditorSettings::get_singleton()->set("_touch_actions_panel_embed_index", embedded_panel_index);
+ EditorSettings::get_singleton()->save();
}
TouchActionsPanel::TouchActionsPanel() {
- Ref panel_style;
- panel_style.instantiate();
- panel_style->set_bg_color(Color(0.1, 0.1, 0.1, 1));
- panel_style->set_border_color(Color(0.3, 0.3, 0.3, 1));
- panel_style->set_border_width_all(3);
- panel_style->set_corner_radius_all(10);
- panel_style->set_content_margin_all(12);
- add_theme_style_override(SceneStringName(panel), panel_style);
+ int panel_mode = EDITOR_GET("interface/touchscreen/touch_actions_panel");
+ is_floating = panel_mode == 2;
- set_position(EDITOR_DEF("_touch_actions_panel_position", Point2(480, 480))); // Dropped it here for no good reason — users can move it anyway.
+ if (is_floating) {
+ Ref panel_style;
+ panel_style.instantiate();
+ panel_style->set_bg_color(Color(0.1, 0.1, 0.1, 1));
+ panel_style->set_border_color(Color(0.3, 0.3, 0.3, 1));
+ panel_style->set_border_width_all(3);
+ panel_style->set_corner_radius_all(10);
+ panel_style->set_content_margin_all(12);
+ add_theme_style_override(SceneStringName(panel), panel_style);
+
+ set_position(EDITOR_DEF("_touch_actions_panel_position", Point2(480, 480))); // Dropped it here for no good reason — users can move it anyway.
+ }
box = memnew(BoxContainer);
box->set_alignment(BoxContainer::ALIGNMENT_CENTER);
box->add_theme_constant_override("separation", 20);
- box->set_vertical(EDITOR_DEF("_touch_actions_panel_vertical_layout", false));
+ if (is_floating) {
+ box->set_vertical(EDITOR_DEF("_touch_actions_panel_vertical_layout", false));
+ } else {
+ box->set_vertical(true);
+ }
add_child(box);
- drag_handle = memnew(TextureRect);
- drag_handle->set_custom_minimum_size(Size2(40, 40));
- drag_handle->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
- drag_handle->connect(SceneStringName(gui_input), callable_mp(this, &TouchActionsPanel::_on_drag_handle_gui_input));
- box->add_child(drag_handle);
+ if (is_floating) {
+ drag_handle = memnew(TextureRect);
+ drag_handle->set_custom_minimum_size(Size2(40, 40));
+ drag_handle->set_stretch_mode(TextureRect::STRETCH_KEEP_CENTERED);
+ drag_handle->connect(SceneStringName(gui_input), callable_mp(this, &TouchActionsPanel::_on_drag_handle_gui_input));
+ box->add_child(drag_handle);
- layout_toggle_button = memnew(Button);
- layout_toggle_button->set_theme_type_variation("FlatMenuButton");
- layout_toggle_button->set_accessibility_name(TTRC("Switch Layout"));
- layout_toggle_button->set_focus_mode(FOCUS_NONE);
- layout_toggle_button->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- layout_toggle_button->connect(SceneStringName(pressed), callable_mp(this, &TouchActionsPanel::_switch_layout));
- box->add_child(layout_toggle_button);
+ layout_toggle_button = memnew(Button);
+ layout_toggle_button->set_theme_type_variation("FlatMenuButton");
+ layout_toggle_button->set_accessibility_name(TTRC("Switch Layout"));
+ layout_toggle_button->set_focus_mode(FOCUS_NONE);
+ layout_toggle_button->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ layout_toggle_button->connect(SceneStringName(pressed), callable_mp(this, &TouchActionsPanel::_switch_layout));
+ box->add_child(layout_toggle_button);
- lock_panel_button = memnew(Button);
- lock_panel_button->set_toggle_mode(true);
- lock_panel_button->set_theme_type_variation("FlatMenuButton");
- lock_panel_button->set_accessibility_name(TTRC("Lock Panel"));
- lock_panel_button->set_focus_mode(FOCUS_NONE);
- lock_panel_button->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
- lock_panel_button->connect(SceneStringName(toggled), callable_mp(this, &TouchActionsPanel::_lock_panel_toggled));
- box->add_child(lock_panel_button);
+ lock_panel_button = memnew(Button);
+ lock_panel_button->set_toggle_mode(true);
+ lock_panel_button->set_theme_type_variation("FlatMenuButton");
+ lock_panel_button->set_accessibility_name(TTRC("Lock Panel"));
+ lock_panel_button->set_focus_mode(FOCUS_NONE);
+ lock_panel_button->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ lock_panel_button->connect(SceneStringName(toggled), callable_mp(this, &TouchActionsPanel::_lock_panel_toggled));
+ box->add_child(lock_panel_button);
+ } else {
+ panel_pos_button = memnew(Button);
+ panel_pos_button->set_theme_type_variation("FlatMenuButton");
+ panel_pos_button->set_accessibility_name(TTRC("Switch Embedded Panel Position"));
+ panel_pos_button->set_focus_mode(FOCUS_NONE);
+ panel_pos_button->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
+ panel_pos_button->connect(SceneStringName(pressed), callable_mp(this, &TouchActionsPanel::_switch_embedded_panel_side));
+ box->add_child(panel_pos_button);
+
+ embedded_panel_index = EDITOR_DEF("_touch_actions_panel_embed_index", 0);
+ }
ColorRect *separator = memnew(ColorRect);
separator->set_color(Color(0.5, 0.5, 0.5));
diff --git a/editor/gui/touch_actions_panel.h b/editor/gui/touch_actions_panel.h
index f8eae906d15..9b4944966c7 100644
--- a/editor/gui/touch_actions_panel.h
+++ b/editor/gui/touch_actions_panel.h
@@ -52,8 +52,9 @@ private:
TextureRect *drag_handle = nullptr;
Button *layout_toggle_button = nullptr;
Button *lock_panel_button = nullptr;
+ Button *panel_pos_button = nullptr;
- bool lock_panel_position = false;
+ bool locked_panel = false;
bool dragging = false;
Vector2 drag_offset;
@@ -67,6 +68,9 @@ private:
bool shift_btn_pressed = false;
bool alt_btn_pressed = false;
+ bool is_floating = false; // Embedded panel mode is default.
+ int embedded_panel_index = 0;
+
void _notification(int p_what);
virtual void input(const Ref &event) override;
@@ -75,8 +79,9 @@ private:
void _on_drag_handle_gui_input(const Ref &p_event);
void _switch_layout();
void _lock_panel_toggled(bool p_pressed);
- Button *_add_new_action_button(const String &p_shortcut, const String &p_name, Key p_keycode = Key::NONE);
+ void _switch_embedded_panel_side();
+ Button *_add_new_action_button(const String &p_shortcut, const String &p_name, Key p_keycode = Key::NONE);
void _add_new_modifier_button(Modifier p_modifier);
void _on_modifier_button_toggled(bool p_pressed, int p_modifier);