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);