diff options
26 files changed, 221 insertions, 62 deletions
diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index 5c0856cb6a..4753b34efb 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -1081,6 +1081,9 @@ <member name="anchor_top" type="float" setter="_set_anchor" getter="get_anchor" default="0.0"> Anchors the top edge of the node to the origin, the center or the end of its parent control. It changes how the top offset updates when the node moves or changes size. You can use one of the [enum Anchor] constants for convenience. </member> + <member name="auto_translate" type="bool" setter="set_auto_translate" getter="is_auto_translating" default="true"> + Toggles if any text should automatically change to its translated version depending on the current locale. + </member> <member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" enum="Control.FocusMode" default="0"> The focus access mode for the control (None, Click or All). Only one Control can be focused at the same time, and it will receive keyboard signals. </member> diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml index c340895130..89736e4a8d 100644 --- a/doc/classes/Input.xml +++ b/doc/classes/Input.xml @@ -63,6 +63,7 @@ </argument> <description> Returns a value between 0 and 1 representing the raw intensity of the given action, ignoring the action's deadzone. In most cases, you should use [method get_action_strength] instead. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. </description> </method> <method name="get_action_strength" qualifiers="const"> @@ -74,6 +75,7 @@ </argument> <description> Returns a value between 0 and 1 representing the intensity of the given action. In a joypad, for example, the further away the axis (analog sticks or L2, R2 triggers) is from the dead zone, the closer the value will be to 1. If the action is mapped to a control that has no axis as the keyboard, the value returned will be 0 or 1. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. </description> </method> <method name="get_axis" qualifiers="const"> @@ -223,6 +225,7 @@ <description> Returns [code]true[/code] when the user starts pressing the action event, meaning it's [code]true[/code] only on the frame that the user pressed down the button. This is useful for code that needs to run only once when an action is pressed, instead of every frame while it's pressed. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. </description> </method> <method name="is_action_just_released" qualifiers="const"> @@ -234,6 +237,7 @@ </argument> <description> Returns [code]true[/code] when the user stops pressing the action event, meaning it's [code]true[/code] only on the frame that the user released the button. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. </description> </method> <method name="is_action_pressed" qualifiers="const"> @@ -245,6 +249,7 @@ </argument> <description> Returns [code]true[/code] if you are pressing the action event. Note that if an action has multiple buttons assigned and more than one of them is pressed, releasing one button will release the action, even if some other button assigned to this action is still pressed. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. </description> </method> <method name="is_joy_button_pressed" qualifiers="const"> diff --git a/doc/classes/InputEvent.xml b/doc/classes/InputEvent.xml index c28c4c4282..9ed965bbf4 100644 --- a/doc/classes/InputEvent.xml +++ b/doc/classes/InputEvent.xml @@ -39,6 +39,7 @@ </argument> <description> Returns a value between 0.0 and 1.0 depending on the given actions' state. Useful for getting the value of events of type [InputEventJoypadMotion]. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. </description> </method> <method name="is_action" qualifiers="const"> @@ -50,6 +51,7 @@ </argument> <description> Returns [code]true[/code] if this input event matches a pre-defined action of any type. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. </description> </method> <method name="is_action_pressed" qualifiers="const"> @@ -63,6 +65,7 @@ </argument> <description> Returns [code]true[/code] if the given action is being pressed (and is not an echo event for [InputEventKey] events, unless [code]allow_echo[/code] is [code]true[/code]). Not relevant for events of type [InputEventMouseMotion] or [InputEventScreenDrag]. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. </description> </method> <method name="is_action_released" qualifiers="const"> @@ -74,6 +77,7 @@ </argument> <description> Returns [code]true[/code] if the given action is released (i.e. not pressed). Not relevant for events of type [InputEventMouseMotion] or [InputEventScreenDrag]. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. </description> </method> <method name="is_action_type" qualifiers="const"> diff --git a/doc/classes/InputMap.xml b/doc/classes/InputMap.xml index 3948ab0208..3292555b4e 100644 --- a/doc/classes/InputMap.xml +++ b/doc/classes/InputMap.xml @@ -113,6 +113,7 @@ </argument> <description> Returns [code]true[/code] if the given event is part of an existing action. This method ignores keyboard modifiers if the given [InputEvent] is not pressed (for proper release detection). See [method action_has_event] if you don't want this behavior. + If [code]exact_match[/code] is [code]false[/code], it ignores the input modifiers for [InputEventKey] and [InputEventMouseButton] events, and the direction for [InputEventJoypadMotion] events. </description> </method> <method name="get_actions"> diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index 390ebfabba..7b21d99d10 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -323,6 +323,9 @@ <members> <member name="always_on_top" type="bool" setter="set_flag" getter="get_flag" default="false"> </member> + <member name="auto_translate" type="bool" setter="set_auto_translate" getter="is_auto_translating" default="true"> + Toggles if any text should automatically change to its translated version depending on the current locale. + </member> <member name="borderless" type="bool" setter="set_flag" getter="get_flag" default="false"> </member> <member name="content_scale_aspect" type="int" setter="set_content_scale_aspect" getter="get_content_scale_aspect" enum="Window.ContentScaleAspect" default="0"> diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index b3b43348d0..c230bdcb73 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -582,6 +582,10 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // Focus theme->set_stylebox("Focus", "EditorStyles", style_widget_focus); + // Use a less opaque color to be less distracting for the 2D and 3D editor viewports. + Ref<StyleBoxFlat> style_widget_focus_viewport = style_widget_focus->duplicate(); + style_widget_focus_viewport->set_border_color(accent_color * Color(1, 1, 1, 0.5)); + theme->set_stylebox("FocusViewport", "EditorStyles", style_widget_focus_viewport); // Menu Ref<StyleBoxFlat> style_menu = style_widget->duplicate(); diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 9bbafcc79d..cd16353d6a 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -2655,7 +2655,7 @@ void CanvasItemEditor::_draw_percentage_at_position(float p_value, Point2 p_posi void CanvasItemEditor::_draw_focus() { // Draw the focus around the base viewport if (viewport->has_focus()) { - get_theme_stylebox(SNAME("Focus"), SNAME("EditorStyles"))->draw(viewport->get_canvas_item(), Rect2(Point2(), viewport->get_size())); + get_theme_stylebox(SNAME("FocusViewport"), SNAME("EditorStyles"))->draw(viewport->get_canvas_item(), Rect2(Point2(), viewport->get_size())); } } @@ -3853,7 +3853,10 @@ void CanvasItemEditor::_notification(int p_what) { key_auto_insert_button->add_theme_color_override("icon_pressed_color", key_auto_color.lerp(Color(1, 0, 0), 0.55)); animation_menu->set_icon(get_theme_icon(SNAME("GuiTabMenuHl"), SNAME("EditorIcons"))); + _update_context_menu_stylebox(); + presets_menu->set_icon(get_theme_icon(SNAME("ControlLayout"), SNAME("EditorIcons"))); + PopupMenu *p = presets_menu->get_popup(); p->clear(); @@ -3954,6 +3957,18 @@ void CanvasItemEditor::edit(CanvasItem *p_canvas_item) { } } +void CanvasItemEditor::_update_context_menu_stylebox() { + // This must be called when the theme changes to follow the new accent color. + Ref<StyleBoxFlat> context_menu_stylebox = memnew(StyleBoxFlat); + const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color("accent_color", "Editor"); + context_menu_stylebox->set_bg_color(accent_color * Color(1, 1, 1, 0.1)); + // Add an underline to the StyleBox, but prevent its minimum vertical size from changing. + context_menu_stylebox->set_border_color(accent_color); + context_menu_stylebox->set_border_width(SIDE_BOTTOM, Math::round(2 * EDSCALE)); + context_menu_stylebox->set_default_margin(SIDE_BOTTOM, 0); + context_menu_container->add_theme_style_override("panel", context_menu_stylebox); +} + void CanvasItemEditor::_update_scrollbars() { updating_scroll = true; @@ -5127,11 +5142,11 @@ void CanvasItemEditor::remove_control_from_info_overlay(Control *p_control) { void CanvasItemEditor::add_control_to_menu_panel(Control *p_control) { ERR_FAIL_COND(!p_control); - hb->add_child(p_control); + hbc_context_menu->add_child(p_control); } void CanvasItemEditor::remove_control_from_menu_panel(Control *p_control) { - hb->remove_child(p_control); + hbc_context_menu->remove_child(p_control); } HSplitContainer *CanvasItemEditor::get_palette_split() { @@ -5516,10 +5531,21 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { p->add_separator(); p->add_check_shortcut(ED_SHORTCUT("canvas_item_editor/preview_canvas_scale", TTR("Preview Canvas Scale"), KEY_MASK_SHIFT | KEY_MASK_CMD | KEY_P), PREVIEW_CANVAS_SCALE); + hb->add_child(memnew(VSeparator)); + + context_menu_container = memnew(PanelContainer); + hbc_context_menu = memnew(HBoxContainer); + context_menu_container->add_child(hbc_context_menu); + // Use a custom stylebox to make contextual menu items stand out from the rest. + // This helps with editor usability as contextual menu items change when selecting nodes, + // even though it may not be immediately obvious at first. + hb->add_child(context_menu_container); + _update_context_menu_stylebox(); + presets_menu = memnew(MenuButton); presets_menu->set_shortcut_context(this); presets_menu->set_text(TTR("Layout")); - hb->add_child(presets_menu); + hbc_context_menu->add_child(presets_menu); presets_menu->hide(); presets_menu->set_switch_on_hover(true); @@ -5533,17 +5559,18 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { anchor_mode_button = memnew(Button); anchor_mode_button->set_flat(true); - hb->add_child(anchor_mode_button); + hbc_context_menu->add_child(anchor_mode_button); anchor_mode_button->set_toggle_mode(true); anchor_mode_button->hide(); anchor_mode_button->connect("toggled", callable_mp(this, &CanvasItemEditor::_button_toggle_anchor_mode)); animation_hb = memnew(HBoxContainer); - hb->add_child(animation_hb); + hbc_context_menu->add_child(animation_hb); animation_hb->add_child(memnew(VSeparator)); animation_hb->hide(); key_loc_button = memnew(Button); + key_loc_button->set_flat(true); key_loc_button->set_toggle_mode(true); key_loc_button->set_pressed(true); key_loc_button->set_focus_mode(FOCUS_NONE); @@ -5552,6 +5579,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { animation_hb->add_child(key_loc_button); key_rot_button = memnew(Button); + key_rot_button->set_flat(true); key_rot_button->set_toggle_mode(true); key_rot_button->set_pressed(true); key_rot_button->set_focus_mode(FOCUS_NONE); @@ -5560,6 +5588,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { animation_hb->add_child(key_rot_button); key_scale_button = memnew(Button); + key_scale_button->set_flat(true); key_scale_button->set_toggle_mode(true); key_scale_button->set_focus_mode(FOCUS_NONE); key_scale_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(ANIM_INSERT_SCALE)); @@ -5567,6 +5596,7 @@ CanvasItemEditor::CanvasItemEditor(EditorNode *p_editor) { animation_hb->add_child(key_scale_button); key_insert_button = memnew(Button); + key_insert_button->set_flat(true); key_insert_button->set_focus_mode(FOCUS_NONE); key_insert_button->connect("pressed", callable_mp(this, &CanvasItemEditor::_popup_callback), varray(ANIM_INSERT_KEY)); key_insert_button->set_tooltip(TTR("Insert keys (based on mask).")); @@ -5733,11 +5763,14 @@ void CanvasItemEditorViewport::_create_preview(const Vector<String> &files) cons preview_node->add_child(sprite); label->show(); label_desc->show(); + label_desc->set_text(TTR("Drag and drop to add as child of current scene's root node.\nHold Ctrl when dropping to add as child of selected node.\nHold Shift when dropping to add as sibling of selected node.\nHold Alt when dropping to add as a different node type.")); } else { if (scene.is_valid()) { Node *instance = scene->instantiate(); if (instance) { preview_node->add_child(instance); + label_desc->show(); + label_desc->set_text(TTR("Drag and drop to add as child of current scene's root node.\nHold Ctrl when dropping to add as child of selected node.\nHold Shift when dropping to add as sibling of selected node.")); } } } @@ -6036,6 +6069,7 @@ bool CanvasItemEditorViewport::_only_packed_scenes_selected() const { void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p_data) { bool is_shift = Input::get_singleton()->is_key_pressed(KEY_SHIFT); + bool is_ctrl = Input::get_singleton()->is_key_pressed(KEY_CTRL); bool is_alt = Input::get_singleton()->is_key_pressed(KEY_ALT); selected_files.clear(); @@ -6047,24 +6081,25 @@ void CanvasItemEditorViewport::drop_data(const Point2 &p_point, const Variant &p return; } - List<Node *> list = editor->get_editor_selection()->get_selected_node_list(); - if (list.size() == 0) { - Node *root_node = editor->get_edited_scene(); + List<Node *> selected_nodes = editor->get_editor_selection()->get_selected_node_list(); + Node *root_node = editor->get_edited_scene(); + if (selected_nodes.size() > 0) { + Node *selected_node = selected_nodes[0]; + target_node = root_node; + if (is_ctrl) { + target_node = selected_node; + } else if (is_shift && selected_node != root_node) { + target_node = selected_node->get_parent(); + } + } else { if (root_node) { - list.push_back(root_node); + target_node = root_node; } else { drop_pos = p_point; target_node = nullptr; } } - if (list.size() > 0) { - target_node = list[0]; - if (is_shift && target_node != editor->get_edited_scene()) { - target_node = target_node->get_parent(); - } - } - drop_pos = p_point; if (is_alt && !_only_packed_scenes_selected()) { @@ -6145,7 +6180,6 @@ CanvasItemEditorViewport::CanvasItemEditorViewport(EditorNode *p_node, CanvasIte canvas_item_editor->get_controls_container()->add_child(label); label_desc = memnew(Label); - label_desc->set_text(TTR("Drag & drop + Shift : Add node as sibling\nDrag & drop + Alt : Change node type")); label_desc->add_theme_color_override("font_color", Color(0.6f, 0.6f, 0.6f, 1)); label_desc->add_theme_color_override("font_shadow_color", Color(0.2f, 0.2f, 0.2f, 1)); label_desc->add_theme_constant_override("shadow_as_outline", 1 * EDSCALE); diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 7b64d0cb5d..d466032588 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -230,6 +230,10 @@ private: HScrollBar *h_scroll; VScrollBar *v_scroll; HBoxContainer *hb; + // Used for secondary menu items which are displayed depending on the currently selected node + // (such as MeshInstance's "Mesh" menu). + PanelContainer *context_menu_container; + HBoxContainer *hbc_context_menu; Map<Control *, Timer *> popup_temporarily_timers; @@ -535,6 +539,7 @@ private: HSplitContainer *palette_split; VSplitContainer *bottom_split; + void _update_context_menu_stylebox(); void _popup_warning_temporarily(Control *p_control, const float p_duration); void _popup_warning_depop(Control *p_control); diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index ebd6615ac1..b1f4baac13 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -2871,7 +2871,7 @@ void Node3DEditorViewport::_draw() { if (surface->has_focus()) { Size2 size = surface->get_size(); Rect2 r = Rect2(Point2(), size); - get_theme_stylebox(SNAME("Focus"), SNAME("EditorStyles"))->draw(surface->get_canvas_item(), r); + get_theme_stylebox(SNAME("FocusViewport"), SNAME("EditorStyles"))->draw(surface->get_canvas_item(), r); } if (cursor.region_select) { @@ -4066,6 +4066,7 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_ } bool is_shift = Input::get_singleton()->is_key_pressed(KEY_SHIFT); + bool is_ctrl = Input::get_singleton()->is_key_pressed(KEY_CTRL); selected_files.clear(); Dictionary d = p_data; @@ -4073,29 +4074,32 @@ void Node3DEditorViewport::drop_data_fw(const Point2 &p_point, const Variant &p_ selected_files = d["files"]; } - List<Node *> list = editor->get_editor_selection()->get_selected_node_list(); - if (list.size() == 0) { - Node *root_node = editor->get_edited_scene(); + List<Node *> selected_nodes = editor->get_editor_selection()->get_selected_node_list(); + Node *root_node = editor->get_edited_scene(); + if (selected_nodes.size() == 1) { + Node *selected_node = selected_nodes[0]; + target_node = root_node; + if (is_ctrl) { + target_node = selected_node; + } else if (is_shift && selected_node != root_node) { + target_node = selected_node->get_parent(); + } + } else if (selected_nodes.size() == 0) { if (root_node) { - list.push_back(root_node); + target_node = root_node; } else { - accept->set_text(TTR("No parent to instance a child at.")); + accept->set_text(TTR("Cannot drag and drop into scene with no root node.")); accept->popup_centered(); _remove_preview(); return; } - } - if (list.size() != 1) { - accept->set_text(TTR("This operation requires a single selected node.")); + } else { + accept->set_text(TTR("Cannot drag and drop into multiple selected nodes.")); accept->popup_centered(); _remove_preview(); return; } - target_node = list[0]; - if (is_shift && target_node != editor->get_edited_scene()) { - target_node = target_node->get_parent(); - } drop_pos = p_point; _perform_drop_data(); @@ -5968,6 +5972,18 @@ void fragment() { _generate_selection_boxes(); } +void Node3DEditor::_update_context_menu_stylebox() { + // This must be called when the theme changes to follow the new accent color. + Ref<StyleBoxFlat> context_menu_stylebox = memnew(StyleBoxFlat); + const Color accent_color = EditorNode::get_singleton()->get_gui_base()->get_theme_color("accent_color", "Editor"); + context_menu_stylebox->set_bg_color(accent_color * Color(1, 1, 1, 0.1)); + // Add an underline to the StyleBox, but prevent its minimum vertical size from changing. + context_menu_stylebox->set_border_color(accent_color); + context_menu_stylebox->set_border_width(SIDE_BOTTOM, Math::round(2 * EDSCALE)); + context_menu_stylebox->set_default_margin(SIDE_BOTTOM, 0); + context_menu_container->add_theme_style_override("panel", context_menu_stylebox); +} + void Node3DEditor::_update_gizmos_menu() { gizmos_menu->clear(); @@ -6535,6 +6551,7 @@ void Node3DEditor::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { _update_theme(); _update_gizmos_menu_theme(); + _update_context_menu_stylebox(); sun_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window"))); environ_title->add_theme_font_override("font", get_theme_font(SNAME("title_font"), SNAME("Window"))); } break; @@ -6583,11 +6600,11 @@ Vector<int> Node3DEditor::get_subgizmo_selection() { } void Node3DEditor::add_control_to_menu_panel(Control *p_control) { - hbc_menu->add_child(p_control); + hbc_context_menu->add_child(p_control); } void Node3DEditor::remove_control_from_menu_panel(Control *p_control) { - hbc_menu->remove_child(p_control); + hbc_context_menu->remove_child(p_control); } void Node3DEditor::set_can_preview(Camera3D *p_preview) { @@ -7170,6 +7187,17 @@ Node3DEditor::Node3DEditor(EditorNode *p_editor) { view_menu->set_shortcut_context(this); hbc_menu->add_child(view_menu); + hbc_menu->add_child(memnew(VSeparator)); + + context_menu_container = memnew(PanelContainer); + hbc_context_menu = memnew(HBoxContainer); + context_menu_container->add_child(hbc_context_menu); + // Use a custom stylebox to make contextual menu items stand out from the rest. + // This helps with editor usability as contextual menu items change when selecting nodes, + // even though it may not be immediately obvious at first. + hbc_menu->add_child(context_menu_container); + _update_context_menu_stylebox(); + p = view_menu->get_popup(); accept = memnew(AcceptDialog); diff --git a/editor/plugins/node_3d_editor_plugin.h b/editor/plugins/node_3d_editor_plugin.h index 0c7a0d14a2..6ac3345daf 100644 --- a/editor/plugins/node_3d_editor_plugin.h +++ b/editor/plugins/node_3d_editor_plugin.h @@ -625,6 +625,10 @@ private: void _update_camera_override_viewport(Object *p_viewport); HBoxContainer *hbc_menu; + // Used for secondary menu items which are displayed depending on the currently selected node + // (such as MeshInstance's "Mesh" menu). + PanelContainer *context_menu_container; + HBoxContainer *hbc_context_menu; void _generate_selection_boxes(); UndoRedo *undo_redo; @@ -632,6 +636,7 @@ private: int camera_override_viewport_id; void _init_indicators(); + void _update_context_menu_stylebox(); void _update_gizmos_menu(); void _update_gizmos_menu_theme(); void _init_grid(); diff --git a/editor/project_export.cpp b/editor/project_export.cpp index fcb714bdf7..14158b02c8 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -1171,7 +1171,7 @@ ProjectExportDialog::ProjectExportDialog() { script_key = memnew(LineEdit); script_key->connect("text_changed", callable_mp(this, &ProjectExportDialog::_script_encryption_key_changed)); script_key_error = memnew(Label); - script_key_error->set_text("- " + TTR("Invalid Encryption Key (must be 64 hexadecimal characters long)")); + script_key_error->set_text(String::utf8("• ") + TTR("Invalid Encryption Key (must be 64 hexadecimal characters long)")); script_key_error->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor"))); sec_vb->add_margin_child(TTR("Encryption Key (256-bits as hexadecimal):"), script_key); sec_vb->add_child(script_key_error); @@ -1250,7 +1250,7 @@ ProjectExportDialog::ProjectExportDialog() { Label *export_error2 = memnew(Label); export_templates_error->add_child(export_error2); export_error2->add_theme_color_override("font_color", EditorNode::get_singleton()->get_gui_base()->get_theme_color(SNAME("error_color"), SNAME("Editor"))); - export_error2->set_text(" - " + TTR("Export templates for this platform are missing:") + " "); + export_error2->set_text(String::utf8("• ") + TTR("Export templates for this platform are missing:") + " "); error_dialog = memnew(AcceptDialog); error_dialog->set_title(TTR("Error")); diff --git a/editor/script_create_dialog.cpp b/editor/script_create_dialog.cpp index 9a4b38db74..4cbc859e0c 100644 --- a/editor/script_create_dialog.cpp +++ b/editor/script_create_dialog.cpp @@ -604,7 +604,7 @@ void ScriptCreateDialog::_path_submitted(const String &p_path) { } void ScriptCreateDialog::_msg_script_valid(bool valid, const String &p_msg) { - error_label->set_text("- " + p_msg); + error_label->set_text(String::utf8("• ") + p_msg); if (valid) { error_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); } else { @@ -613,7 +613,7 @@ void ScriptCreateDialog::_msg_script_valid(bool valid, const String &p_msg) { } void ScriptCreateDialog::_msg_path_valid(bool valid, const String &p_msg) { - path_error_label->set_text("- " + p_msg); + path_error_label->set_text(String::utf8("• ") + p_msg); if (valid) { path_error_label->add_theme_color_override("font_color", get_theme_color(SNAME("success_color"), SNAME("Editor"))); } else { diff --git a/misc/dist/linux/org.godotengine.Godot.xml b/misc/dist/linux/org.godotengine.Godot.xml index 989db6afaa..d4452018c4 100644 --- a/misc/dist/linux/org.godotengine.Godot.xml +++ b/misc/dist/linux/org.godotengine.Godot.xml @@ -2,6 +2,7 @@ <mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info"> <mime-type type="application/x-godot-project"> <comment>Godot Engine project</comment> + <sub-class-of type="text/plain"/> <icon name="x-godot-project"/> <glob pattern="project.godot"/> </mime-type> diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 30d023606f..24d43858b5 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -5531,7 +5531,10 @@ struct EditorSceneImporterGLTFInterpolate<Quaternion> { template <class T> T GLTFDocument::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, const float p_time, const GLTFAnimation::Interpolation p_interp) { ERR_FAIL_COND_V(!p_values.size(), T()); - ERR_FAIL_COND_V(p_times.size() != p_values.size(), p_values[0]); + if (p_times.size() != p_values.size()) { + ERR_PRINT_ONCE("The interpolated values are not corresponding to its times."); + return p_values[0]; + } //could use binary search, worth it? int idx = -1; for (int i = 0; i < p_times.size(); i++) { diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 00b847168d..e623309888 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -137,7 +137,7 @@ void AnimationNode::make_invalid(const String &p_reason) { if (state->invalid_reasons != String()) { state->invalid_reasons += "\n"; } - state->invalid_reasons += "- " + p_reason; + state->invalid_reasons += String::utf8("• ") + p_reason; } float AnimationNode::blend_input(int p_input, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) { diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 4b6c0ef697..9cdf3bf210 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -79,7 +79,7 @@ void Button::_notification(int p_what) { update(); } break; case NOTIFICATION_TRANSLATION_CHANGED: { - xl_text = tr(text); + xl_text = atr(text); _shape(); minimum_size_changed(); @@ -355,7 +355,7 @@ void Button::_shape() { void Button::set_text(const String &p_text) { if (text != p_text) { text = p_text; - xl_text = tr(text); + xl_text = atr(text); _shape(); update(); diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index c19ee849d3..4179c8b246 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -495,6 +495,20 @@ bool Control::is_layout_rtl() const { return data.is_rtl; } +void Control::set_auto_translate(bool p_enable) { + if (p_enable == data.auto_translate) { + return; + } + + data.auto_translate = p_enable; + + notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); +} + +bool Control::is_auto_translating() const { + return data.auto_translate; +} + void Control::_clear_size_warning() { data.size_warning = false; } @@ -2784,6 +2798,9 @@ void Control::_bind_methods() { ClassDB::bind_method(D_METHOD("get_layout_direction"), &Control::get_layout_direction); ClassDB::bind_method(D_METHOD("is_layout_rtl"), &Control::is_layout_rtl); + ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Control::set_auto_translate); + ClassDB::bind_method(D_METHOD("is_auto_translating"), &Control::is_auto_translating); + BIND_VMETHOD(MethodInfo("_structured_text_parser", PropertyInfo(Variant::ARRAY, "args"), PropertyInfo(Variant::STRING, "text"))); BIND_VMETHOD(MethodInfo("_gui_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); @@ -2848,10 +2865,13 @@ void Control::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_h_size_flags", "get_h_size_flags"); ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_vertical", PROPERTY_HINT_FLAGS, "Fill,Expand,Shrink Center,Shrink End"), "set_v_size_flags", "get_v_size_flags"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size_flags_stretch_ratio", PROPERTY_HINT_RANGE, "0,20,0.01,or_greater"), "set_stretch_ratio", "get_stretch_ratio"); + ADD_GROUP("Theme", "theme_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_type_variation", PROPERTY_HINT_ENUM_SUGGESTION), "set_theme_type_variation", "get_theme_type_variation"); - ADD_GROUP("", ""); + + ADD_GROUP("Auto Translate", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating"); BIND_ENUM_CONSTANT(FOCUS_NONE); BIND_ENUM_CONSTANT(FOCUS_CLICK); diff --git a/scene/gui/control.h b/scene/gui/control.h index 3779f9b308..8d669c7a6d 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -182,6 +182,8 @@ private: bool is_rtl_dirty = true; bool is_rtl = false; + bool auto_translate = true; + real_t rotation = 0.0; Vector2 scale = Vector2(1, 1); Vector2 pivot_offset; @@ -351,6 +353,10 @@ public: LayoutDirection get_layout_direction() const; virtual bool is_layout_rtl() const; + void set_auto_translate(bool p_enable); + bool is_auto_translating() const; + _FORCE_INLINE_ String atr(const String p_string) const { return is_auto_translating() ? tr(p_string) : p_string; }; + /* POSITIONING */ void set_anchors_preset(LayoutPreset p_preset, bool p_keep_offsets = true); diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index 06faf3fa3e..1603989243 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -221,7 +221,7 @@ void Label::_update_visible() { void Label::_notification(int p_what) { if (p_what == NOTIFICATION_TRANSLATION_CHANGED) { - String new_text = tr(text); + String new_text = atr(text); if (new_text == xl_text) { return; //nothing new } @@ -509,7 +509,7 @@ void Label::set_text(const String &p_string) { return; } text = p_string; - xl_text = tr(p_string); + xl_text = atr(p_string); dirty = true; if (percent_visible < 1) { visible_chars = get_total_character_count() * percent_visible; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 19ffcfca6d..68e9171c15 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -597,7 +597,7 @@ void LineEdit::_notification(int p_what) { update(); } break; case NOTIFICATION_TRANSLATION_CHANGED: { - placeholder_translated = tr(placeholder); + placeholder_translated = atr(placeholder); _shape(); update(); } break; @@ -1364,7 +1364,7 @@ String LineEdit::get_text() const { void LineEdit::set_placeholder(String p_text) { placeholder = p_text; - placeholder_translated = tr(placeholder); + placeholder_translated = atr(placeholder); _shape(); update(); } diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 7790a0970c..490548fce0 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -722,7 +722,7 @@ void PopupMenu::_notification(int p_what) { case Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED: case NOTIFICATION_TRANSLATION_CHANGED: { for (int i = 0; i < items.size(); i++) { - items.write[i].xl_text = tr(items[i].text); + items.write[i].xl_text = atr(items[i].text); items.write[i].dirty = true; _shape_item(i); } @@ -807,7 +807,7 @@ void PopupMenu::_notification(int p_what) { #define ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel) \ item.text = p_label; \ - item.xl_text = tr(p_label); \ + item.xl_text = atr(p_label); \ item.id = p_id == -1 ? items.size() : p_id; \ item.accel = p_accel; @@ -887,7 +887,7 @@ void PopupMenu::add_multistate_item(const String &p_label, int p_max_states, int ERR_FAIL_COND_MSG(p_shortcut.is_null(), "Cannot add item with invalid Shortcut."); \ _ref_shortcut(p_shortcut); \ item.text = p_shortcut->get_name(); \ - item.xl_text = tr(item.text); \ + item.xl_text = atr(item.text); \ item.id = p_id == -1 ? items.size() : p_id; \ item.shortcut = p_shortcut; \ item.shortcut_is_global = p_global; @@ -956,7 +956,7 @@ void PopupMenu::add_icon_radio_check_shortcut(const Ref<Texture2D> &p_icon, cons void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, int p_id) { Item item; item.text = p_label; - item.xl_text = tr(p_label); + item.xl_text = atr(p_label); item.id = p_id == -1 ? items.size() : p_id; item.submenu = p_submenu; items.push_back(item); @@ -973,7 +973,7 @@ void PopupMenu::add_submenu_item(const String &p_label, const String &p_submenu, void PopupMenu::set_item_text(int p_idx, const String &p_text) { ERR_FAIL_INDEX(p_idx, items.size()); items.write[p_idx].text = p_text; - items.write[p_idx].xl_text = tr(p_text); + items.write[p_idx].xl_text = atr(p_text); _shape_item(p_idx); control->update(); @@ -1402,7 +1402,7 @@ void PopupMenu::add_separator(const String &p_text, int p_id) { sep.id = p_id; if (p_text != String()) { sep.text = p_text; - sep.xl_text = tr(p_text); + sep.xl_text = atr(p_text); } items.push_back(sep); control->update(); diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 8f84585f9b..4b8c4b3e16 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -550,7 +550,7 @@ void TabContainer::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, in // Draw the tab contents. Control *control = Object::cast_to<Control>(tabs[p_index]); - String text = control->has_meta("_tab_name") ? String(tr(String(control->get_meta("_tab_name")))) : String(tr(control->get_name())); + String text = control->has_meta("_tab_name") ? String(atr(String(control->get_meta("_tab_name")))) : String(atr(control->get_name())); int x_content = tab_rect.position.x + p_tab_style->get_margin(SIDE_LEFT); int top_margin = p_tab_style->get_margin(SIDE_TOP); @@ -584,7 +584,8 @@ void TabContainer::_refresh_texts() { int font_size = get_theme_font_size(SNAME("font_size")); for (int i = 0; i < tabs.size(); i++) { Control *control = Object::cast_to<Control>(tabs[i]); - String text = control->has_meta("_tab_name") ? String(tr(String(control->get_meta("_tab_name")))) : String(tr(control->get_name())); + String text = control->has_meta("_tab_name") ? String(atr(String(control->get_meta("_tab_name")))) : String(atr(control->get_name())); + Ref<TextLine> name; name.instantiate(); name->set_direction(rtl ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); @@ -648,7 +649,7 @@ int TabContainer::_get_tab_width(int p_index) const { // Get the width of the text displayed on the tab. Ref<Font> font = get_theme_font(SNAME("font")); int font_size = get_theme_font_size(SNAME("font_size")); - String text = control->has_meta("_tab_name") ? String(tr(String(control->get_meta("_tab_name")))) : String(tr(control->get_name())); + String text = control->has_meta("_tab_name") ? String(atr(String(control->get_meta("_tab_name")))) : String(atr(control->get_name())); int width = font->get_string_size(text, font_size).width; // Add space for a tab icon. diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index 9e8fe27ffb..103860ad78 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -239,7 +239,7 @@ void Tabs::_shape(int p_tab) { Ref<Font> font = get_theme_font(SNAME("font")); int font_size = get_theme_font_size(SNAME("font_size")); - tabs.write[p_tab].xl_text = tr(tabs[p_tab].text); + tabs.write[p_tab].xl_text = atr(tabs[p_tab].text); tabs.write[p_tab].text_buf->clear(); if (tabs[p_tab].text_direction == Control::TEXT_DIRECTION_INHERITED) { tabs.write[p_tab].text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); @@ -529,7 +529,7 @@ bool Tabs::get_offset_buttons_visible() const { void Tabs::set_tab_title(int p_tab, const String &p_title) { ERR_FAIL_INDEX(p_tab, tabs.size()); tabs.write[p_tab].text = p_title; - tabs.write[p_tab].xl_text = tr(p_title); + tabs.write[p_tab].xl_text = atr(p_title); _shape(p_tab); update(); minimum_size_changed(); @@ -742,7 +742,7 @@ void Tabs::_on_mouse_exited() { void Tabs::add_tab(const String &p_str, const Ref<Texture2D> &p_icon) { Tab t; t.text = p_str; - t.xl_text = tr(p_str); + t.xl_text = atr(p_str); t.text_buf.instantiate(); t.text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); t.text_buf->add_string(t.xl_text, get_theme_font(SNAME("font")), get_theme_font_size(SNAME("font_size")), Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index 7b74d049fb..8e7182df46 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -260,7 +260,7 @@ void Viewport::_sub_window_update(Window *p_window) { int close_h_ofs = p_window->get_theme_constant(SNAME("close_h_ofs")); int close_v_ofs = p_window->get_theme_constant(SNAME("close_v_ofs")); - TextLine title_text = TextLine(p_window->get_title(), title_font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); + TextLine title_text = TextLine(p_window->atr(p_window->get_title()), title_font, font_size, Dictionary(), TranslationServer::get_singleton()->get_tool_locale()); title_text.set_width(r.size.width - panel->get_minimum_size().x - close_h_ofs); title_text.set_direction(p_window->is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR); int x = (r.size.width - title_text.get_size().x) / 2; @@ -1558,6 +1558,7 @@ void Viewport::_gui_show_tooltip() { // If no custom tooltip is given, use a default implementation. if (!base_tooltip) { gui.tooltip_label = memnew(TooltipLabel); + gui.tooltip_label->set_auto_translate(gui.tooltip_control->is_auto_translating()); gui.tooltip_label->set_text(tooltip_text); base_tooltip = gui.tooltip_label; panel->connect("mouse_entered", callable_mp(this, &Viewport::_gui_cancel_tooltip)); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index bed8a1ba56..1f1da7cefb 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -42,9 +42,8 @@ void Window::set_title(const String &p_title) { if (embedder) { embedder->_sub_window_update(this); - } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { - DisplayServer::get_singleton()->window_set_title(p_title, window_id); + DisplayServer::get_singleton()->window_set_title(atr(p_title), window_id); } } @@ -233,7 +232,7 @@ void Window::_make_window() { DisplayServer::get_singleton()->window_set_current_screen(current_screen, window_id); DisplayServer::get_singleton()->window_set_max_size(max_size, window_id); DisplayServer::get_singleton()->window_set_min_size(min_size, window_id); - DisplayServer::get_singleton()->window_set_title(tr(title), window_id); + DisplayServer::get_singleton()->window_set_title(atr(title), window_id); DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id); _update_window_size(); @@ -761,6 +760,12 @@ void Window::_notification(int p_what) { } } break; case NOTIFICATION_TRANSLATION_CHANGED: { + if (embedder) { + embedder->_sub_window_update(this); + } else if (window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_title(atr(title), window_id); + } + child_controls_changed(); } break; case NOTIFICATION_EXIT_TREE: { @@ -1341,6 +1346,20 @@ bool Window::is_layout_rtl() const { } } +void Window::set_auto_translate(bool p_enable) { + if (p_enable == auto_translate) { + return; + } + + auto_translate = p_enable; + + notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); +} + +bool Window::is_auto_translating() const { + return auto_translate; +} + void Window::_validate_property(PropertyInfo &property) const { if (property.name == "theme_type_variation") { List<StringName> names; @@ -1467,6 +1486,9 @@ void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("get_layout_direction"), &Window::get_layout_direction); ClassDB::bind_method(D_METHOD("is_layout_rtl"), &Window::is_layout_rtl); + ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Window::set_auto_translate); + ClassDB::bind_method(D_METHOD("is_auto_translating"), &Window::is_auto_translating); + ClassDB::bind_method(D_METHOD("popup", "rect"), &Window::popup, DEFVAL(Rect2i())); ClassDB::bind_method(D_METHOD("popup_on_parent", "parent_rect"), &Window::popup_on_parent); ClassDB::bind_method(D_METHOD("popup_centered_ratio", "ratio"), &Window::popup_centered_ratio, DEFVAL(0.8)); @@ -1478,6 +1500,7 @@ void Window::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Windowed,Minimized,Maximized,Fullscreen"), "set_mode", "get_mode"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "current_screen"), "set_current_screen", "get_current_screen"); + ADD_GROUP("Flags", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_controls"), "set_wrap_controls", "is_wrapping_controls"); @@ -1488,17 +1511,23 @@ void Window::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "always_on_top"), "set_flag", "get_flag", FLAG_ALWAYS_ON_TOP); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "transparent"), "set_flag", "get_flag", FLAG_TRANSPARENT); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "unfocusable"), "set_flag", "get_flag", FLAG_NO_FOCUS); + ADD_GROUP("Limits", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "min_size"), "set_min_size", "get_min_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "max_size"), "set_max_size", "get_max_size"); + ADD_GROUP("Content Scale", "content_scale_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2I, "content_scale_size"), "set_content_scale_size", "get_content_scale_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_mode", PROPERTY_HINT_ENUM, "Disabled,Canvas Items,Viewport"), "set_content_scale_mode", "get_content_scale_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_aspect", PROPERTY_HINT_ENUM, "Ignore,Keep,Keep Width,Keep Height,Expand"), "set_content_scale_aspect", "get_content_scale_aspect"); + ADD_GROUP("Theme", "theme_"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), "set_theme", "get_theme"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "theme_type_variation", PROPERTY_HINT_ENUM_SUGGESTION), "set_theme_type_variation", "get_theme_type_variation"); + ADD_GROUP("Auto Translate", ""); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate"), "set_auto_translate", "is_auto_translating"); + ADD_SIGNAL(MethodInfo("window_input", PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"))); ADD_SIGNAL(MethodInfo("files_dropped", PropertyInfo(Variant::PACKED_STRING_ARRAY, "files"))); ADD_SIGNAL(MethodInfo("mouse_entered")); diff --git a/scene/main/window.h b/scene/main/window.h index e92b5e22ed..7013694a06 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -103,6 +103,8 @@ private: LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED; + bool auto_translate = true; + void _update_child_controls(); Size2i content_scale_size; @@ -256,6 +258,10 @@ public: LayoutDirection get_layout_direction() const; bool is_layout_rtl() const; + void set_auto_translate(bool p_enable); + bool is_auto_translating() const; + _FORCE_INLINE_ String atr(const String p_string) const { return is_auto_translating() ? tr(p_string) : p_string; }; + Rect2i get_usable_parent_rect() const; Ref<Texture2D> get_theme_icon(const StringName &p_name, const StringName &p_theme_type = StringName()) const; |