Compare commits

...

16 commits

Author SHA1 Message Date
Thaddeus Crews
60710df3b6
Merge pull request #94047 from KoBeWi/resource_printer
Improve `to_string()` and add it to Resource
2025-10-16 12:48:11 -05:00
Thaddeus Crews
769505fef3
Merge pull request #111703 from timothyqiu/glsl-utf8
Fix error when compute shaders contain Unicode comment
2025-10-16 12:48:10 -05:00
Thaddeus Crews
94818a5313
Merge pull request #111509 from passivestar/orbit-snapping
Implement orbit snapping in 3D viewport
2025-10-16 12:48:09 -05:00
Thaddeus Crews
026efbb0f5
Merge pull request #111733 from DarioSamo/rendering-device-shutdown-fix
Do not begin a new frame during RenderingDevice's shutdown.
2025-10-16 12:48:08 -05:00
Thaddeus Crews
f3ebee1f79
Merge pull request #111211 from superherointj/enet-warning
System-provided ENet warning
2025-10-16 12:48:07 -05:00
Thaddeus Crews
9e96c7d9b8
Merge pull request #110378 from timothyqiu/rtl-preview
Make text-related nodes translation domain aware
2025-10-16 12:48:05 -05:00
Thaddeus Crews
2edad6860a
Merge pull request #111658 from brycehutchings/bryceh_d3d12_native_handle_fixes
Fix D3D12 rendering device driver returning pointers to internal types for get_resource_native_handle instead of proper D3D12 primitives
2025-10-16 12:48:04 -05:00
Thaddeus Crews
13404515fb
Merge pull request #111523 from Joy-less/fix-IsStaticallyResolvable
Fix `IsStaticallyResolvable`
2025-10-16 12:48:03 -05:00
Dario
af584b1ba8 Do not begin a new frame during RenderingDevice's shutdown. 2025-10-16 13:47:18 -03:00
passivestar
d739700178 Implement orbit snapping in 3D viewport 2025-10-16 12:46:14 +04:00
Haoyu Qiu
73afe696d2 Fix error when compute shaders contain Unicode comment 2025-10-16 11:21:14 +08:00
Bryce Hutchings
1f7e7de82b Fix D3D12 driver returning internal types to RenderingDevice's texture_get_native_handle/get_driver_resource 2025-10-15 10:35:38 -07:00
Joyless
a5ce9c3706 Fix IsStaticallyResolvable 2025-10-11 21:20:01 +01:00
kobewi
e6783dbdd1 Improve to_string() and add it to Resource 2025-10-09 00:54:38 +02:00
superherointj
24f23c5cdb System-provided ENet warning
Warning from documentation: https://github.com/godotengine/godot/tree/master/thirdparty#enet
2025-10-08 08:51:09 -03:00
Haoyu Qiu
172c80df67 Make text-related nodes translation domain aware
- Makes `is_layout_rtl()` translation domain aware
- Makes various text-drawing controls translation domain aware
- Makes translation preview use the project's fallback locale when disabled
2025-09-22 09:39:14 +08:00
48 changed files with 368 additions and 282 deletions

View file

@ -277,7 +277,7 @@ String InputEventWithModifiers::as_text() const {
}
}
String InputEventWithModifiers::to_string() {
String InputEventWithModifiers::_to_string() {
return as_text();
}
@ -488,7 +488,7 @@ String InputEventKey::as_text() const {
return mods_text.is_empty() ? kc : mods_text + "+" + kc;
}
String InputEventKey::to_string() {
String InputEventKey::_to_string() {
String p = is_pressed() ? "true" : "false";
String e = is_echo() ? "true" : "false";
@ -848,7 +848,7 @@ String InputEventMouseButton::as_text() const {
return full_string;
}
String InputEventMouseButton::to_string() {
String InputEventMouseButton::_to_string() {
String p = is_pressed() ? "true" : "false";
String canceled_state = is_canceled() ? "true" : "false";
String d = double_click ? "true" : "false";
@ -988,7 +988,7 @@ String InputEventMouseMotion::as_text() const {
return vformat(RTR("Mouse motion at position (%s) with velocity (%s)"), String(get_position()), String(get_velocity()));
}
String InputEventMouseMotion::to_string() {
String InputEventMouseMotion::_to_string() {
BitField<MouseButtonMask> mouse_button_mask = get_button_mask();
String button_mask_string = itos((int64_t)mouse_button_mask);
@ -1184,7 +1184,7 @@ String InputEventJoypadMotion::as_text() const {
return vformat(RTR("Joypad Motion on Axis %d (%s) with Value %.2f"), axis, desc, axis_value);
}
String InputEventJoypadMotion::to_string() {
String InputEventJoypadMotion::_to_string() {
return vformat("InputEventJoypadMotion: axis=%d, axis_value=%.2f", axis, axis_value);
}
@ -1303,7 +1303,7 @@ String InputEventJoypadButton::as_text() const {
return text;
}
String InputEventJoypadButton::to_string() {
String InputEventJoypadButton::_to_string() {
String p = is_pressed() ? "true" : "false";
return vformat("InputEventJoypadButton: button_index=%d, pressed=%s, pressure=%.2f", button_index, p, pressure);
}
@ -1386,7 +1386,7 @@ String InputEventScreenTouch::as_text() const {
return vformat(RTR("Screen %s at (%s) with %s touch points"), status, String(get_position()), itos(index));
}
String InputEventScreenTouch::to_string() {
String InputEventScreenTouch::_to_string() {
String p = pressed ? "true" : "false";
String canceled_state = canceled ? "true" : "false";
String double_tap_string = double_tap ? "true" : "false";
@ -1514,7 +1514,7 @@ String InputEventScreenDrag::as_text() const {
return vformat(RTR("Screen dragged with %s touch points at position (%s) with velocity of (%s)"), itos(index), String(get_position()), String(get_velocity()));
}
String InputEventScreenDrag::to_string() {
String InputEventScreenDrag::_to_string() {
return vformat("InputEventScreenDrag: index=%d, position=(%s), relative=(%s), velocity=(%s), pressure=%.2f, tilt=(%s), pen_inverted=(%s)", index, String(get_position()), String(get_relative()), String(get_velocity()), get_pressure(), String(get_tilt()), get_pen_inverted());
}
@ -1656,7 +1656,7 @@ String InputEventAction::as_text() const {
return String();
}
String InputEventAction::to_string() {
String InputEventAction::_to_string() {
String p = is_pressed() ? "true" : "false";
return vformat("InputEventAction: action=\"%s\", pressed=%s", action, p);
}
@ -1727,7 +1727,7 @@ String InputEventMagnifyGesture::as_text() const {
return vformat(RTR("Magnify Gesture at (%s) with factor %s"), String(get_position()), rtos(get_factor()));
}
String InputEventMagnifyGesture::to_string() {
String InputEventMagnifyGesture::_to_string() {
return vformat("InputEventMagnifyGesture: factor=%.2f, position=(%s)", factor, String(get_position()));
}
@ -1769,7 +1769,7 @@ String InputEventPanGesture::as_text() const {
return vformat(RTR("Pan Gesture at (%s) with delta (%s)"), String(get_position()), String(get_delta()));
}
String InputEventPanGesture::to_string() {
String InputEventPanGesture::_to_string() {
return vformat("InputEventPanGesture: delta=(%s), position=(%s)", String(get_delta()), String(get_position()));
}
@ -1850,7 +1850,7 @@ String InputEventMIDI::as_text() const {
return vformat(RTR("MIDI Input on Channel=%s Message=%s"), itos(channel), itos((int64_t)message));
}
String InputEventMIDI::to_string() {
String InputEventMIDI::_to_string() {
String ret;
switch (message) {
case MIDIMessage::NOTE_ON:
@ -1926,7 +1926,7 @@ String InputEventShortcut::as_text() const {
return vformat(RTR("Input Event with Shortcut=%s"), shortcut->get_as_text());
}
String InputEventShortcut::to_string() {
String InputEventShortcut::_to_string() {
ERR_FAIL_COND_V(shortcut.is_null(), "None");
return vformat("InputEventShortcut: shortcut=%s", shortcut->get_as_text());

View file

@ -146,7 +146,7 @@ public:
BitField<KeyModifierMask> get_modifiers_mask() const;
virtual String as_text() const override;
virtual String to_string() override;
virtual String _to_string() override;
InputEventWithModifiers() {}
};
@ -200,7 +200,7 @@ public:
virtual String as_text_key_label() const;
virtual String as_text_location() const;
virtual String as_text() const override;
virtual String to_string() override;
virtual String _to_string() override;
static Ref<InputEventKey> create_reference(Key p_keycode_with_modifier_masks, bool p_physical = false);
@ -263,7 +263,7 @@ public:
virtual bool is_action_type() const override { return true; }
virtual String as_text() const override;
virtual String to_string() override;
virtual String _to_string() override;
InputEventType get_type() const final override { return InputEventType::MOUSE_BUTTON; }
@ -308,7 +308,7 @@ public:
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
virtual String as_text() const override;
virtual String to_string() override;
virtual String _to_string() override;
virtual bool accumulate(const Ref<InputEvent> &p_event) override;
@ -337,7 +337,7 @@ public:
virtual bool is_action_type() const override { return true; }
virtual String as_text() const override;
virtual String to_string() override;
virtual String _to_string() override;
// The default device ID is `InputMap::ALL_DEVICES`.
static Ref<InputEventJoypadMotion> create_reference(JoyAxis p_axis, float p_value, int p_device = -1);
@ -370,7 +370,7 @@ public:
virtual bool is_action_type() const override { return true; }
virtual String as_text() const override;
virtual String to_string() override;
virtual String _to_string() override;
// The default device ID is `InputMap::ALL_DEVICES`.
static Ref<InputEventJoypadButton> create_reference(JoyButton p_btn_index, int p_device = -1);
@ -404,7 +404,7 @@ public:
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
virtual String as_text() const override;
virtual String to_string() override;
virtual String _to_string() override;
InputEventType get_type() const final override { return InputEventType::SCREEN_TOUCH; }
@ -456,7 +456,7 @@ public:
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
virtual String as_text() const override;
virtual String to_string() override;
virtual String _to_string() override;
virtual bool accumulate(const Ref<InputEvent> &p_event) override;
@ -495,7 +495,7 @@ public:
virtual bool is_action_type() const override { return true; }
virtual String as_text() const override;
virtual String to_string() override;
virtual String _to_string() override;
InputEventType get_type() const final override { return InputEventType::ACTION; }
@ -528,7 +528,7 @@ public:
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
virtual String as_text() const override;
virtual String to_string() override;
virtual String _to_string() override;
InputEventType get_type() const final override { return InputEventType::MAGNIFY_GESTURE; }
@ -548,7 +548,7 @@ public:
virtual Ref<InputEvent> xformed_by(const Transform2D &p_xform, const Vector2 &p_local_ofs = Vector2()) const override;
virtual String as_text() const override;
virtual String to_string() override;
virtual String _to_string() override;
InputEventType get_type() const final override { return InputEventType::PAN_GESTURE; }
@ -596,7 +596,7 @@ public:
int get_controller_value() const;
virtual String as_text() const override;
virtual String to_string() override;
virtual String _to_string() override;
InputEventType get_type() const final override { return InputEventType::MIDI; }
@ -616,7 +616,7 @@ public:
Ref<Shortcut> get_shortcut();
virtual String as_text() const override;
virtual String to_string() override;
virtual String _to_string() override;
InputEventType get_type() const final override { return InputEventType::SHORTCUT; }

View file

@ -660,6 +660,10 @@ void Resource::reset_local_to_scene() {
// Restores the state as if setup_local_to_scene() hadn't been called.
}
String Resource::_to_string() {
return (name.is_empty() ? "" : String(name) + " ") + "(" + path_cache + "):" + Object::_to_string();
}
Node *(*Resource::_get_local_scene_func)() = nullptr;
void (*Resource::_update_configuration_warning)() = nullptr;

View file

@ -122,6 +122,7 @@ protected:
GDVIRTUAL0(_reset_state);
virtual Ref<Resource> _duplicate(const DuplicateParams &p_params) const;
virtual String _to_string() override;
public:
static Node *(*_get_local_scene_func)(); // Used by the editor.

View file

@ -1044,7 +1044,7 @@ String Object::to_string() {
return ret;
}
}
return "<" + get_class() + "#" + itos(get_instance_id()) + ">";
return _to_string();
}
void Object::set_script(const Variant &p_script) {
@ -1688,6 +1688,19 @@ bool Object::_uses_signal_mutex() const {
return true;
}
String Object::_get_locale() const {
TranslationServer *ts = TranslationServer::get_singleton();
const StringName domain_name = get_translation_domain();
if (ts->has_domain(domain_name)) {
const Ref<TranslationDomain> domain = ts->get_or_add_domain(domain_name);
const String &overridden = domain->get_locale_override();
if (!overridden.is_empty()) {
return overridden;
}
}
return ts->get_locale();
}
void Object::_set_bind(const StringName &p_set, const Variant &p_value) {
set(p_set, p_value);
}
@ -1826,6 +1839,10 @@ void Object::notify_property_list_changed() {
emit_signal(CoreStringName(property_list_changed));
}
String Object::_to_string() {
return "<" + get_class() + "#" + itos(get_instance_id()) + ">";
}
void Object::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_class"), &Object::get_class);
ClassDB::bind_method(D_METHOD("is_class", "class"), &Object::is_class);

View file

@ -738,6 +738,7 @@ protected:
void _notification_backward(int p_notification);
virtual void _notification_forwardv(int p_notification) {}
virtual void _notification_backwardv(int p_notification) {}
virtual String _to_string();
static void _bind_methods();
static void _bind_compatibility_methods() {}
@ -801,6 +802,9 @@ protected:
virtual bool _uses_signal_mutex() const;
// Internal helper to get the current locale, taking into account the translation domain.
String _get_locale() const;
#ifdef TOOLS_ENABLED
struct VirtualMethodTracker {
void **method;
@ -916,7 +920,7 @@ public:
}
}
virtual String to_string();
String to_string();
// Used mainly by script, get and set all INCLUDING string.
virtual Variant getvar(const Variant &p_key, bool *r_valid = nullptr) const;

View file

@ -84,5 +84,5 @@ public:
Variant get_member_variable_value(int p_frame_index, int p_variable_index) const;
String format(int p_indent_all = 0, int p_indent_frames = 4) const;
virtual String to_string() override { return format(); }
virtual String _to_string() override { return format(); }
};

View file

@ -407,6 +407,9 @@
<member name="editors/3d/navigation/zoom_style" type="int" setter="" getter="">
The mouse cursor movement direction to use when zooming by moving the mouse. This does not affect zooming with the mouse wheel.
</member>
<member name="editors/3d/navigation_feel/angle_snap_threshold" type="float" setter="" getter="">
The angle threshold for snapping camera rotation to 45-degree angles while orbiting with [kbd]Alt[/kbd] held.
</member>
<member name="editors/3d/navigation_feel/orbit_inertia" type="float" setter="" getter="">
The inertia to use when orbiting in the 3D editor. Higher values make the camera start and stop slower, which looks smoother but adds latency.
</member>

View file

@ -1163,6 +1163,7 @@
<constant name="DRIVER_RESOURCE_COMMAND_QUEUE" value="3" enum="DriverResource">
The main graphics-compute command queue ([code]rid[/code] parameter is ignored).
- Vulkan: [code]VkQueue[/code].
- D3D12: [code]ID3D12CommandQueue[/code].
- Metal: [code]MTLCommandQueue[/code].
</constant>
<constant name="DRIVER_RESOURCE_QUEUE_FAMILY" value="4" enum="DriverResource">
@ -1171,6 +1172,7 @@
</constant>
<constant name="DRIVER_RESOURCE_TEXTURE" value="5" enum="DriverResource">
- Vulkan: [code]VkImage[/code].
- D3D12: [code]ID3D12Resource[/code].
</constant>
<constant name="DRIVER_RESOURCE_TEXTURE_VIEW" value="6" enum="DriverResource">
The view of an owned or shared texture.

View file

@ -5751,14 +5751,18 @@ uint64_t RenderingDeviceDriverD3D12::get_resource_native_handle(DriverResource p
return 0;
}
case DRIVER_RESOURCE_COMMAND_QUEUE: {
return (uint64_t)p_driver_id.id;
const CommandQueueInfo *cmd_queue_info = (const CommandQueueInfo *)p_driver_id.id;
return (uint64_t)cmd_queue_info->d3d_queue.Get();
}
case DRIVER_RESOURCE_QUEUE_FAMILY: {
return 0;
}
case DRIVER_RESOURCE_TEXTURE: {
const TextureInfo *tex_info = (const TextureInfo *)p_driver_id.id;
return (uint64_t)tex_info->main_texture;
if (tex_info->main_texture) {
tex_info = tex_info->main_texture;
}
return (uint64_t)tex_info->resource;
} break;
case DRIVER_RESOURCE_TEXTURE_VIEW: {
const TextureInfo *tex_info = (const TextureInfo *)p_driver_id.id;

View file

@ -455,6 +455,10 @@ void EditorNode::_update_from_settings() {
String current_fallback_locale = GLOBAL_GET("internationalization/locale/fallback");
if (current_fallback_locale != TranslationServer::get_singleton()->get_fallback_locale()) {
TranslationServer::get_singleton()->set_fallback_locale(current_fallback_locale);
Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_main_domain();
if (!domain->is_enabled()) {
domain->set_locale_override(current_fallback_locale);
}
scene_root->propagate_notification(Control::NOTIFICATION_LAYOUT_DIRECTION_CHANGED);
}
@ -4203,8 +4207,15 @@ void EditorNode::set_preview_locale(const String &p_locale) {
// Texts set in the editor could be identifiers that should never be translated.
// So we need to disable translation entirely.
Ref<TranslationDomain> main_domain = TranslationServer::get_singleton()->get_main_domain();
main_domain->set_enabled(!p_locale.is_empty());
main_domain->set_locale_override(p_locale);
if (p_locale.is_empty()) {
// Disable preview. Use the fallback locale.
main_domain->set_enabled(false);
main_domain->set_locale_override(TranslationServer::get_singleton()->get_fallback_locale());
} else {
// Preview a specific locale.
main_domain->set_enabled(true);
main_domain->set_locale_override(p_locale);
}
_translation_resources_changed();
}
@ -7638,7 +7649,10 @@ EditorNode::EditorNode() {
ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &EditorNode::_update_from_settings));
GDExtensionManager::get_singleton()->connect("extensions_reloaded", callable_mp(this, &EditorNode::_gdextensions_reloaded));
TranslationServer::get_singleton()->get_main_domain()->set_enabled(false);
Ref<TranslationDomain> domain = TranslationServer::get_singleton()->get_main_domain();
domain->set_enabled(false);
domain->set_locale_override(TranslationServer::get_singleton()->get_fallback_locale());
// Load settings.
if (!EditorSettings::get_singleton()) {
EditorSettings::create();

View file

@ -1213,7 +1213,7 @@ void Node3DEditorViewport::_update_name() {
} break;
}
if (auto_orthogonal) {
if (orthogonal && auto_orthogonal) {
// TRANSLATORS: This will be appended to the view name when Auto Orthogonal is enabled.
name += " " + TTR("[auto]");
}
@ -2541,27 +2541,32 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) {
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_down", p_event)) {
// Clamp rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
cursor.x_rot = CLAMP(cursor.x_rot - Math::PI / 12.0, -1.57, 1.57);
cursor.unsnapped_x_rot = cursor.x_rot;
view_type = VIEW_TYPE_USER;
_update_name();
}
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_up", p_event)) {
// Clamp rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
cursor.x_rot = CLAMP(cursor.x_rot + Math::PI / 12.0, -1.57, 1.57);
cursor.unsnapped_x_rot = cursor.x_rot;
view_type = VIEW_TYPE_USER;
_update_name();
}
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_right", p_event)) {
cursor.y_rot -= Math::PI / 12.0;
cursor.unsnapped_y_rot = cursor.y_rot;
view_type = VIEW_TYPE_USER;
_update_name();
}
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_left", p_event)) {
cursor.y_rot += Math::PI / 12.0;
cursor.unsnapped_y_rot = cursor.y_rot;
view_type = VIEW_TYPE_USER;
_update_name();
}
if (ED_IS_SHORTCUT("spatial_editor/orbit_view_180", p_event)) {
cursor.y_rot += Math::PI;
cursor.unsnapped_y_rot = cursor.y_rot;
view_type = VIEW_TYPE_USER;
_update_name();
}
@ -2766,30 +2771,69 @@ void Node3DEditorViewport::_nav_orbit(Ref<InputEventWithModifiers> p_event, cons
return;
}
if (orthogonal && auto_orthogonal) {
_menu_option(VIEW_PERSPECTIVE);
}
const real_t degrees_per_pixel = EDITOR_GET("editors/3d/navigation_feel/orbit_sensitivity");
const real_t radians_per_pixel = Math::deg_to_rad(degrees_per_pixel);
const bool invert_y_axis = EDITOR_GET("editors/3d/navigation/invert_y_axis");
const bool invert_x_axis = EDITOR_GET("editors/3d/navigation/invert_x_axis");
if (invert_y_axis) {
cursor.x_rot -= p_relative.y * radians_per_pixel;
} else {
cursor.x_rot += p_relative.y * radians_per_pixel;
}
// Clamp the Y rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
cursor.x_rot = CLAMP(cursor.x_rot, -1.57, 1.57);
cursor.unsnapped_x_rot += p_relative.y * radians_per_pixel * (invert_y_axis ? -1 : 1);
cursor.unsnapped_x_rot = CLAMP(cursor.unsnapped_x_rot, -1.57, 1.57);
cursor.unsnapped_y_rot += p_relative.x * radians_per_pixel * (invert_x_axis ? -1 : 1);
if (invert_x_axis) {
cursor.y_rot -= p_relative.x * radians_per_pixel;
} else {
cursor.y_rot += p_relative.x * radians_per_pixel;
cursor.x_rot = cursor.unsnapped_x_rot;
cursor.y_rot = cursor.unsnapped_y_rot;
if (_is_nav_modifier_pressed("spatial_editor/viewport_orbit_snap_modifier_1") &&
_is_nav_modifier_pressed("spatial_editor/viewport_orbit_snap_modifier_2")) {
const real_t snap_angle = Math::deg_to_rad(45.0);
const real_t snap_threshold = Math::deg_to_rad((real_t)EDITOR_GET("editors/3d/navigation_feel/angle_snap_threshold"));
real_t x_rot_snapped = Math::snapped(cursor.unsnapped_x_rot, snap_angle);
real_t y_rot_snapped = Math::snapped(cursor.unsnapped_y_rot, snap_angle);
real_t x_dist = Math::abs(cursor.unsnapped_x_rot - x_rot_snapped);
real_t y_dist = Math::abs(cursor.unsnapped_y_rot - y_rot_snapped);
if (x_dist < snap_threshold && y_dist < snap_threshold) {
cursor.x_rot = x_rot_snapped;
cursor.y_rot = y_rot_snapped;
real_t y_rot_wrapped = Math::wrapf(y_rot_snapped, (real_t)-Math::PI, (real_t)Math::PI);
if (Math::abs(x_rot_snapped) < snap_threshold) {
if (Math::abs(y_rot_wrapped) < snap_threshold) {
view_type = VIEW_TYPE_FRONT;
} else if (Math::abs(Math::abs(y_rot_wrapped) - Math::PI) < snap_threshold) {
view_type = VIEW_TYPE_REAR;
} else if (Math::abs(y_rot_wrapped - Math::PI / 2.0) < snap_threshold) {
view_type = VIEW_TYPE_LEFT;
} else if (Math::abs(y_rot_wrapped + Math::PI / 2.0) < snap_threshold) {
view_type = VIEW_TYPE_RIGHT;
} else {
// Only switch to ortho for 90-degree views.
return;
}
_set_auto_orthogonal();
_update_name();
} else if (Math::abs(Math::abs(x_rot_snapped) - Math::PI / 2.0) < snap_threshold) {
if (Math::abs(y_rot_wrapped) < snap_threshold ||
Math::abs(Math::abs(y_rot_wrapped) - Math::PI) < snap_threshold ||
Math::abs(y_rot_wrapped - Math::PI / 2.0) < snap_threshold ||
Math::abs(y_rot_wrapped + Math::PI / 2.0) < snap_threshold) {
view_type = x_rot_snapped > 0 ? VIEW_TYPE_TOP : VIEW_TYPE_BOTTOM;
_set_auto_orthogonal();
_update_name();
}
}
return;
}
}
view_type = VIEW_TYPE_USER;
_update_name();
if (orthogonal && auto_orthogonal) {
_menu_option(VIEW_PERSPECTIVE);
}
}
void Node3DEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, const Vector2 &p_relative) {
@ -2817,8 +2861,10 @@ void Node3DEditorViewport::_nav_look(Ref<InputEventWithModifiers> p_event, const
}
// Clamp the Y rotation to roughly -90..90 degrees so the user can't look upside-down and end up disoriented.
cursor.x_rot = CLAMP(cursor.x_rot, -1.57, 1.57);
cursor.unsnapped_x_rot = cursor.x_rot;
cursor.y_rot += p_relative.x * radians_per_pixel;
cursor.unsnapped_y_rot = cursor.y_rot;
// Look is like the opposite of Orbit: the focus point rotates around the camera
Transform3D camera_transform = to_camera_transform(cursor);
@ -3803,6 +3849,8 @@ void Node3DEditorViewport::_apply_camera_transform_to_cursor() {
cursor.x_rot = -camera_transform.basis.get_euler().x;
cursor.y_rot = -camera_transform.basis.get_euler().y;
cursor.unsnapped_x_rot = cursor.x_rot;
cursor.unsnapped_y_rot = cursor.y_rot;
}
void Node3DEditorViewport::_menu_option(int p_option) {
@ -3811,6 +3859,8 @@ void Node3DEditorViewport::_menu_option(int p_option) {
case VIEW_TOP: {
cursor.y_rot = 0;
cursor.x_rot = Math::PI / 2.0;
cursor.unsnapped_y_rot = cursor.y_rot;
cursor.unsnapped_x_rot = cursor.x_rot;
set_message(TTR("Top View."), 2);
view_type = VIEW_TYPE_TOP;
_set_auto_orthogonal();
@ -3820,6 +3870,8 @@ void Node3DEditorViewport::_menu_option(int p_option) {
case VIEW_BOTTOM: {
cursor.y_rot = 0;
cursor.x_rot = -Math::PI / 2.0;
cursor.unsnapped_y_rot = cursor.y_rot;
cursor.unsnapped_x_rot = cursor.x_rot;
set_message(TTR("Bottom View."), 2);
view_type = VIEW_TYPE_BOTTOM;
_set_auto_orthogonal();
@ -3829,6 +3881,8 @@ void Node3DEditorViewport::_menu_option(int p_option) {
case VIEW_LEFT: {
cursor.x_rot = 0;
cursor.y_rot = Math::PI / 2.0;
cursor.unsnapped_x_rot = cursor.x_rot;
cursor.unsnapped_y_rot = cursor.y_rot;
set_message(TTR("Left View."), 2);
view_type = VIEW_TYPE_LEFT;
_set_auto_orthogonal();
@ -3838,6 +3892,8 @@ void Node3DEditorViewport::_menu_option(int p_option) {
case VIEW_RIGHT: {
cursor.x_rot = 0;
cursor.y_rot = -Math::PI / 2.0;
cursor.unsnapped_x_rot = cursor.x_rot;
cursor.unsnapped_y_rot = cursor.y_rot;
set_message(TTR("Right View."), 2);
view_type = VIEW_TYPE_RIGHT;
_set_auto_orthogonal();
@ -3847,6 +3903,8 @@ void Node3DEditorViewport::_menu_option(int p_option) {
case VIEW_FRONT: {
cursor.x_rot = 0;
cursor.y_rot = 0;
cursor.unsnapped_x_rot = cursor.x_rot;
cursor.unsnapped_y_rot = cursor.y_rot;
set_message(TTR("Front View."), 2);
view_type = VIEW_TYPE_FRONT;
_set_auto_orthogonal();
@ -3856,6 +3914,8 @@ void Node3DEditorViewport::_menu_option(int p_option) {
case VIEW_REAR: {
cursor.x_rot = 0;
cursor.y_rot = Math::PI;
cursor.unsnapped_x_rot = cursor.x_rot;
cursor.unsnapped_y_rot = cursor.y_rot;
set_message(TTR("Rear View."), 2);
view_type = VIEW_TYPE_REAR;
_set_auto_orthogonal();
@ -4485,9 +4545,11 @@ void Node3DEditorViewport::set_state(const Dictionary &p_state) {
}
if (p_state.has("x_rotation")) {
cursor.x_rot = p_state["x_rotation"];
cursor.unsnapped_x_rot = cursor.x_rot;
}
if (p_state.has("y_rotation")) {
cursor.y_rot = p_state["y_rotation"];
cursor.unsnapped_y_rot = cursor.y_rot;
}
if (p_state.has("distance")) {
cursor.distance = p_state["distance"];
@ -6056,6 +6118,8 @@ Node3DEditorViewport::Node3DEditorViewport(Node3DEditor *p_spatial_editor, int p
// Registering with Key::NONE intentionally creates an empty Array.
register_shortcut_action("spatial_editor/viewport_orbit_modifier_1", TTRC("Viewport Orbit Modifier 1"), Key::NONE);
register_shortcut_action("spatial_editor/viewport_orbit_modifier_2", TTRC("Viewport Orbit Modifier 2"), Key::NONE);
register_shortcut_action("spatial_editor/viewport_orbit_snap_modifier_1", TTRC("Viewport Orbit Snap Modifier 1"), Key::ALT);
register_shortcut_action("spatial_editor/viewport_orbit_snap_modifier_2", TTRC("Viewport Orbit Snap Modifier 2"), Key::NONE);
register_shortcut_action("spatial_editor/viewport_pan_modifier_1", TTRC("Viewport Pan Modifier 1"), Key::SHIFT);
register_shortcut_action("spatial_editor/viewport_pan_modifier_2", TTRC("Viewport Pan Modifier 2"), Key::NONE);
register_shortcut_action("spatial_editor/viewport_zoom_modifier_1", TTRC("Viewport Zoom Modifier 1"), Key::SHIFT);

View file

@ -409,6 +409,7 @@ private:
struct Cursor {
Vector3 pos;
real_t x_rot, y_rot, distance, fov_scale;
real_t unsnapped_x_rot, unsnapped_y_rot;
Vector3 eye_pos; // Used in freelook mode
bool region_select;
Point2 region_begin, region_end;
@ -417,6 +418,8 @@ private:
// These rotations place the camera in +X +Y +Z, aka south east, facing north west.
x_rot = 0.5;
y_rot = -0.5;
unsnapped_x_rot = x_rot;
unsnapped_y_rot = y_rot;
distance = 4;
fov_scale = 1.0;
region_select = false;

View file

@ -936,6 +936,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/3d/navigation_feel/orbit_inertia", 0.0, "0,1,0.001")
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/3d/navigation_feel/translation_inertia", 0.05, "0,1,0.001")
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/3d/navigation_feel/zoom_inertia", 0.05, "0,1,0.001")
EDITOR_SETTING(Variant::FLOAT, PROPERTY_HINT_RANGE, "editors/3d/navigation_feel/angle_snap_threshold", 10.0, "1,20,0.1,degrees")
_initial_set("editors/3d/navigation/show_viewport_rotation_gizmo", true);
_initial_set("editors/3d/navigation/show_viewport_navigation_gizmo", DisplayServer::get_singleton()->is_touchscreen_available());

View file

@ -58,7 +58,7 @@ Vector<uint8_t> compile_glslang_shader(RenderingDeviceCommons::ShaderStage p_sta
glslang::EShTargetLanguageVersion TargetVersion = (glslang::EShTargetLanguageVersion)p_spirv_version;
glslang::TShader shader(stages[p_stage]);
CharString cs = p_source_code.ascii();
CharString cs = p_source_code.utf8();
const char *cs_strings = cs.get_data();
std::string preamble = "";

View file

@ -37,6 +37,18 @@ partial class ExportedProperties
/// </summary>
public new static readonly global::Godot.StringName @LamdaPropertyString = "LamdaPropertyString";
/// <summary>
/// Cached name for the 'PrimaryCtorParameter' property.
/// </summary>
public new static readonly global::Godot.StringName @PrimaryCtorParameter = "PrimaryCtorParameter";
/// <summary>
/// Cached name for the 'ConstantMath' property.
/// </summary>
public new static readonly global::Godot.StringName @ConstantMath = "ConstantMath";
/// <summary>
/// Cached name for the 'StaticStringAddition' property.
/// </summary>
public new static readonly global::Godot.StringName @StaticStringAddition = "StaticStringAddition";
/// <summary>
/// Cached name for the 'PropertyBoolean' property.
/// </summary>
public new static readonly global::Godot.StringName @PropertyBoolean = "PropertyBoolean";
@ -317,6 +329,18 @@ partial class ExportedProperties
this.@LamdaPropertyString = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value);
return true;
}
if (name == PropertyName.@PrimaryCtorParameter) {
this.@PrimaryCtorParameter = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value);
return true;
}
if (name == PropertyName.@ConstantMath) {
this.@ConstantMath = global::Godot.NativeInterop.VariantUtils.ConvertTo<float>(value);
return true;
}
if (name == PropertyName.@StaticStringAddition) {
this.@StaticStringAddition = global::Godot.NativeInterop.VariantUtils.ConvertTo<string>(value);
return true;
}
if (name == PropertyName.@PropertyBoolean) {
this.@PropertyBoolean = global::Godot.NativeInterop.VariantUtils.ConvertTo<bool>(value);
return true;
@ -599,6 +623,18 @@ partial class ExportedProperties
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.@LamdaPropertyString);
return true;
}
if (name == PropertyName.@PrimaryCtorParameter) {
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.@PrimaryCtorParameter);
return true;
}
if (name == PropertyName.@ConstantMath) {
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<float>(this.@ConstantMath);
return true;
}
if (name == PropertyName.@StaticStringAddition) {
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<string>(this.@StaticStringAddition);
return true;
}
if (name == PropertyName.@PropertyBoolean) {
value = global::Godot.NativeInterop.VariantUtils.CreateFrom<bool>(this.@PropertyBoolean);
return true;
@ -870,6 +906,9 @@ partial class ExportedProperties
properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.@FullPropertyString_Complex, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.@_lamdaPropertyString, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4096, exported: false));
properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.@LamdaPropertyString, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.@PrimaryCtorParameter, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)3, name: PropertyName.@ConstantMath, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)4, name: PropertyName.@StaticStringAddition, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)1, name: PropertyName.@PropertyBoolean, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.@PropertyChar, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));
properties.Add(new(type: (global::Godot.Variant.Type)2, name: PropertyName.@PropertySByte, hint: (global::Godot.PropertyHint)0, hintString: "", usage: (global::Godot.PropertyUsageFlags)4102, exported: true));

View file

@ -11,7 +11,7 @@ partial class ExportedProperties
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
internal new static global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant> GetGodotPropertyDefaultValues()
{
var values = new global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>(64);
var values = new global::System.Collections.Generic.Dictionary<global::Godot.StringName, global::Godot.Variant>(67);
string __NotGenerateComplexLamdaProperty_default_value = default;
values.Add(PropertyName.@NotGenerateComplexLamdaProperty, global::Godot.Variant.From<string>(__NotGenerateComplexLamdaProperty_default_value));
string __NotGenerateLamdaNoFieldProperty_default_value = default;
@ -26,6 +26,12 @@ partial class ExportedProperties
values.Add(PropertyName.@FullPropertyString_Complex, global::Godot.Variant.From<string>(__FullPropertyString_Complex_default_value));
string __LamdaPropertyString_default_value = "LamdaPropertyString";
values.Add(PropertyName.@LamdaPropertyString, global::Godot.Variant.From<string>(__LamdaPropertyString_default_value));
string __PrimaryCtorParameter_default_value = default;
values.Add(PropertyName.@PrimaryCtorParameter, global::Godot.Variant.From<string>(__PrimaryCtorParameter_default_value));
float __ConstantMath_default_value = 2 * global::Godot.Mathf.Pi;
values.Add(PropertyName.@ConstantMath, global::Godot.Variant.From<float>(__ConstantMath_default_value));
string __StaticStringAddition_default_value = string.Empty + string.Empty;
values.Add(PropertyName.@StaticStringAddition, global::Godot.Variant.From<string>(__StaticStringAddition_default_value));
bool __PropertyBoolean_default_value = true;
values.Add(PropertyName.@PropertyBoolean, global::Godot.Variant.From<bool>(__PropertyBoolean_default_value));
char __PropertyChar_default_value = 'f';

View file

@ -1,7 +1,7 @@
using Godot;
using System;
public partial class ExportedProperties : GodotObject
public partial class ExportedProperties(string primaryCtorParameter) : GodotObject
{
// Do not generate default value
private String _notGeneratePropertyString = new string("not generate");
@ -91,6 +91,18 @@ public partial class ExportedProperties : GodotObject
set => _lamdaPropertyString = value;
}
// Primary Constructor Parameter
[Export]
public String PrimaryCtorParameter { get; set; } = primaryCtorParameter;
// Constant Math Expression
[Export]
public Single ConstantMath { get; set; } = 2 * Mathf.Pi;
// Static Strings Addition
[Export]
public string StaticStringAddition { get; set; } = string.Empty + string.Empty;
// Auto Property
[Export] private Boolean PropertyBoolean { get; set; } = true;
[Export] private Char PropertyChar { get; set; } = 'f';

View file

@ -336,7 +336,11 @@ namespace Godot.SourceGenerators
if (initializer != null)
{
var sm = context.Compilation.GetSemanticModel(initializer.SyntaxTree);
value = initializer.Value.FullQualifiedSyntax(sm);
var initializerValue = initializer.Value;
if (!IsStaticallyResolvable(initializerValue, sm))
value = "default";
else
value = initializer.Value.FullQualifiedSyntax(sm);
}
exportedMembers.Add(new ExportedPropertyMetadata(
@ -424,109 +428,28 @@ namespace Godot.SourceGenerators
private static bool IsStaticallyResolvable(ExpressionSyntax expression, SemanticModel semanticModel)
{
// Handle literals (e.g., `10`, `"string"`, `true`, etc.)
if (expression is LiteralExpressionSyntax)
// Find non-static node in expression
foreach (SyntaxNode descendant in expression.DescendantNodesAndSelf())
{
return true;
}
// Handle negative literals (e.g., `-10`)
if (expression is PrefixUnaryExpressionSyntax { Operand: LiteralExpressionSyntax } &&
expression.Kind() == SyntaxKind.UnaryMinusExpression)
{
return true;
}
// Handle identifiers (e.g., variable names)
if (expression is IdentifierNameSyntax identifier)
{
var symbolInfo = semanticModel.GetSymbolInfo(identifier).Symbol;
// Ensure it's a static member
return symbolInfo is { IsStatic: true };
}
// Handle member access (e.g., `MyClass.StaticValue`)
if (expression is MemberAccessExpressionSyntax memberAccess)
{
var symbolInfo = semanticModel.GetSymbolInfo(memberAccess).Symbol;
// Ensure it's referring to a static member
return symbolInfo is { IsStatic: true };
}
// Handle object creation expressions (e.g., `new Vector2(1.0f, 2.0f)`)
if (expression is ObjectCreationExpressionSyntax objectCreation)
{
// Recursively ensure all its arguments are self-contained
if (objectCreation.ArgumentList == null)
// Constant nodes are static
if (semanticModel.GetConstantValue(descendant).HasValue)
{
return true;
continue;
}
foreach (var argument in objectCreation.ArgumentList.Arguments)
// Check non-static symbol
SymbolInfo symbolInfo = semanticModel.GetSymbolInfo(descendant);
if (symbolInfo.Symbol is ISymbol symbol)
{
if (!IsStaticallyResolvable(argument.Expression, semanticModel))
if (symbol.Kind is SymbolKind.Local or SymbolKind.Parameter)
{
return false;
}
}
return true;
}
if (expression is ImplicitObjectCreationExpressionSyntax)
{
return true;
}
if (expression is InvocationExpressionSyntax invocationExpression)
{
// Resolve the method being invoked
var symbolInfo = semanticModel.GetSymbolInfo(invocationExpression).Symbol;
if (symbolInfo is IMethodSymbol methodSymbol)
{
// Ensure the method is static
if (methodSymbol.IsStatic)
{
return true;
}
}
}
if (expression is InterpolatedStringExpressionSyntax interpolatedString)
{
foreach (var content in interpolatedString.Contents)
{
if (content is not InterpolationSyntax interpolation)
{
continue;
}
// Analyze the expression inside `${...}`
var interpolatedExpression = interpolation.Expression;
if (!IsStaticallyResolvable(interpolatedExpression, semanticModel))
{
return false;
}
}
return true;
}
if (expression is InitializerExpressionSyntax initializerExpressionSyntax)
{
foreach (var content in initializerExpressionSyntax.Expressions)
{
if (!IsStaticallyResolvable(content, semanticModel))
{
return false;
}
}
return true;
}
// Handle other expressions conservatively (e.g., method calls, instance references, etc.)
return false;
// No non-static nodes found
return true;
}
private static bool MemberHasNodeType(ITypeSymbol memberType, MarshalType marshalType)

View file

@ -205,7 +205,7 @@ public:
bool has_java_method(const StringName &p_method) const;
#ifdef ANDROID_ENABLED
virtual String to_string() override;
virtual String _to_string() override;
#endif
JavaClass();
@ -232,7 +232,7 @@ public:
bool has_java_method(const StringName &p_method) const;
#ifdef ANDROID_ENABLED
virtual String to_string() override;
virtual String _to_string() override;
jobject get_instance() { return instance; }

View file

@ -826,7 +826,7 @@ Ref<JavaClass> JavaClass::get_java_parent_class() const {
return ret;
}
String JavaClass::to_string() {
String JavaClass::_to_string() {
return "<JavaClass:" + java_class_name + ">";
}
@ -874,11 +874,11 @@ Ref<JavaClass> JavaObject::get_java_class() const {
return base_class;
}
String JavaObject::to_string() {
String JavaObject::_to_string() {
if (base_class.is_valid() && instance) {
return "<JavaObject:" + base_class->java_class_name + " \"" + (String)call("toString") + "\">";
}
return RefCounted::to_string();
return RefCounted::_to_string();
}
bool JavaObject::has_java_method(const StringName &p_method) const {

View file

@ -247,6 +247,9 @@ def configure(env: "SConsEnvironment"):
if not env["builtin_enet"]:
env.ParseConfig("pkg-config libenet --cflags --libs")
print_warning(
"System-provided ENet has its functionality limited to IPv4 only and no DTLS support, unless patched for Godot."
)
if not env["builtin_zstd"]:
env.ParseConfig("pkg-config libzstd --cflags --libs")

View file

@ -215,11 +215,8 @@ void Label3D::_notification(int p_what) {
window->disconnect("size_changed", callable_mp(this, &Label3D::_font_changed));
} break;
case NOTIFICATION_TRANSLATION_CHANGED: {
String new_text = atr(text);
if (new_text == xl_text) {
return; // Nothing new.
}
xl_text = new_text;
// Language update might change the appearance of some characters.
xl_text = atr(text);
dirty_text = true;
_queue_update();
} break;
@ -483,8 +480,9 @@ void Label3D::_shape() {
TS->shaped_text_clear(text_rid);
TS->shaped_text_set_direction(text_rid, text_direction);
String txt = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text;
TS->shaped_text_add_string(text_rid, txt, font->get_rids(), font_size, font->get_opentype_features(), language);
const String &lang = language.is_empty() ? _get_locale() : language;
String txt = uppercase ? TS->string_to_upper(xl_text, lang) : xl_text;
TS->shaped_text_add_string(text_rid, txt, font->get_rids(), font_size, font->get_opentype_features(), lang);
TypedArray<Vector3i> stt;
if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {

View file

@ -459,8 +459,8 @@ Variant Tween::interpolate_variant(const Variant &p_initial_val, const Variant &
return ret;
}
String Tween::to_string() {
String ret = Object::to_string();
String Tween::_to_string() {
String ret = Object::_to_string();
Node *node = get_bound_node();
if (node) {
ret += vformat(" (bound to %s)", node->get_name());

View file

@ -140,10 +140,9 @@ private:
protected:
static void _bind_methods();
virtual String _to_string() override;
public:
virtual String to_string() override;
Ref<PropertyTweener> tween_property(const Object *p_target, const NodePath &p_property, Variant p_to, double p_duration);
Ref<IntervalTweener> tween_interval(double p_time);
Ref<CallbackTweener> tween_callback(const Callable &p_callback);

View file

@ -566,7 +566,8 @@ void Button::_shape(Ref<TextParagraph> p_paragraph, String p_text) const {
} else {
p_paragraph->set_direction((TextServer::Direction)text_direction);
}
p_paragraph->add_string(p_text, font, font_size, language);
const String &lang = language.is_empty() ? _get_locale() : language;
p_paragraph->add_string(p_text, font, font_size, lang);
p_paragraph->set_text_overrun_behavior(overrun_behavior);
}

View file

@ -188,13 +188,14 @@ void CodeEdit::_notification(int p_what) {
code_completion_line_ofs = CLAMP((code_completion_force_item_center < 0 ? code_completion_current_selected : code_completion_force_item_center) - lines / 2, 0, code_completion_options_count - lines);
RenderingServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(code_completion_rect.position.x, code_completion_rect.position.y + (code_completion_current_selected - code_completion_line_ofs) * row_height), Size2(code_completion_rect.size.width, row_height)), theme_cache.code_completion_selected_color);
const String &lang = _get_locale();
for (int i = 0; i < lines; i++) {
int l = code_completion_line_ofs + i;
ERR_CONTINUE(l < 0 || l >= code_completion_options_count);
Ref<TextLine> tl;
tl.instantiate();
tl->add_string(code_completion_options[l].display, theme_cache.font, theme_cache.font_size);
tl->add_string(code_completion_options[l].display, theme_cache.font, theme_cache.font_size, lang);
int yofs = (row_height - tl->get_size().y) / 2;
Point2 title_pos(code_completion_rect.position.x, code_completion_rect.position.y + i * row_height + yofs);
@ -1521,14 +1522,15 @@ void CodeEdit::_line_number_draw_callback(int p_line, int p_gutter, const Rect2
if (E) {
text_rid = E->value;
} else {
const String &lang = _get_locale();
String fc = String::num_int64(p_line + 1).lpad(line_number_digits, line_number_padding);
if (is_localizing_numeral_system()) {
fc = TS->format_number(fc);
fc = TS->format_number(fc, lang);
}
text_rid = TS->create_shaped_text();
if (theme_cache.font.is_valid()) {
TS->shaped_text_add_string(text_rid, fc, theme_cache.font->get_rids(), theme_cache.font_size, theme_cache.font->get_opentype_features());
TS->shaped_text_add_string(text_rid, fc, theme_cache.font->get_rids(), theme_cache.font_size, theme_cache.font->get_opentype_features(), lang);
}
line_number_text_cache.insert(p_line, text_rid);
}

View file

@ -36,7 +36,6 @@
#include "core/input/input_map.h"
#include "core/os/os.h"
#include "core/string/string_builder.h"
#include "core/string/translation_server.h"
#include "scene/gui/scroll_container.h"
#include "scene/main/canvas_layer.h"
#include "scene/main/window.h"
@ -3548,12 +3547,9 @@ bool Control::is_layout_rtl() const {
} else if (proj_root_layout_direction == 2) {
data.is_rtl = true;
} else if (proj_root_layout_direction == 3) {
String locale = OS::get_singleton()->get_locale();
data.is_rtl = TS->is_locale_right_to_left(locale);
data.is_rtl = TS->is_locale_right_to_left(OS::get_singleton()->get_locale());
} else {
const Ref<Translation> &t = TranslationServer::get_singleton()->get_translation_object(TranslationServer::get_singleton()->get_locale());
String locale = t.is_valid() ? t->get_locale() : TranslationServer::get_singleton()->get_fallback_locale();
data.is_rtl = TS->is_locale_right_to_left(locale);
data.is_rtl = TS->is_locale_right_to_left(_get_locale());
}
return data.is_rtl;
}
@ -3564,8 +3560,9 @@ bool Control::is_layout_rtl() const {
return data.is_rtl;
}
#endif // TOOLS_ENABLED
const StringName domain_name = get_translation_domain();
Node *parent_node = get_parent();
while (parent_node) {
while (parent_node && domain_name == parent_node->get_translation_domain()) {
Control *parent_control = Object::cast_to<Control>(parent_node);
if (parent_control) {
data.is_rtl = parent_control->is_layout_rtl();
@ -3588,22 +3585,19 @@ bool Control::is_layout_rtl() const {
String locale = OS::get_singleton()->get_locale();
data.is_rtl = TS->is_locale_right_to_left(locale);
} else {
String locale = TranslationServer::get_singleton()->get_tool_locale();
data.is_rtl = TS->is_locale_right_to_left(locale);
data.is_rtl = TS->is_locale_right_to_left(_get_locale());
}
} else if (data.layout_dir == LAYOUT_DIRECTION_APPLICATION_LOCALE) {
if (GLOBAL_GET_CACHED(bool, "internationalization/rendering/force_right_to_left_layout_direction")) {
data.is_rtl = true;
} else {
String locale = TranslationServer::get_singleton()->get_tool_locale();
data.is_rtl = TS->is_locale_right_to_left(locale);
data.is_rtl = TS->is_locale_right_to_left(_get_locale());
}
} else if (data.layout_dir == LAYOUT_DIRECTION_SYSTEM_LOCALE) {
if (GLOBAL_GET_CACHED(bool, "internationalization/rendering/force_right_to_left_layout_direction")) {
const_cast<Control *>(this)->data.is_rtl = true;
data.is_rtl = true;
} else {
String locale = OS::get_singleton()->get_locale();
const_cast<Control *>(this)->data.is_rtl = TS->is_locale_right_to_left(locale);
data.is_rtl = TS->is_locale_right_to_left(OS::get_singleton()->get_locale());
}
} else {
data.is_rtl = (data.layout_dir == LAYOUT_DIRECTION_RTL);

View file

@ -488,7 +488,8 @@ void FoldableContainer::_shape() {
}
text_buf->set_horizontal_alignment(_get_actual_alignment());
text_buf->set_text_overrun_behavior(overrun_behavior);
text_buf->add_string(atr(title), font, font_size, language);
const String &lang = language.is_empty() ? _get_locale() : language;
text_buf->add_string(atr(title), font, font_size, lang);
}
HorizontalAlignment FoldableContainer::_get_actual_alignment() const {

View file

@ -43,7 +43,8 @@ void ItemList::_shape_text(int p_idx) {
} else {
item.text_buf->set_direction((TextServer::Direction)item.text_direction);
}
item.text_buf->add_string(item.xl_text, theme_cache.font, theme_cache.font_size, item.language);
const String &lang = item.language.is_empty() ? _get_locale() : item.language;
item.text_buf->add_string(item.xl_text, theme_cache.font, theme_cache.font_size, lang);
if (icon_mode == ICON_MODE_TOP && max_text_lines > 0) {
item.text_buf->set_break_flags(TextServer::BREAK_MANDATORY | TextServer::BREAK_WORD_BOUND | TextServer::BREAK_GRAPHEME_BOUND | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES);
} else {

View file

@ -136,6 +136,8 @@ int Label::get_line_height(int p_line) const {
}
void Label::_shape() const {
const String &lang = language.is_empty() ? _get_locale() : language;
Ref<StyleBox> style = theme_cache.normal_style;
int width = (get_size().width - style->get_minimum_size().width);
@ -149,7 +151,7 @@ void Label::_shape() const {
}
paragraphs.clear();
String txt = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text;
String txt = (uppercase) ? TS->string_to_upper(xl_text, lang) : xl_text;
if (visible_chars >= 0 && visible_chars_behavior == TextServer::VC_CHARS_BEFORE_SHAPING) {
txt = txt.substr(0, visible_chars);
}
@ -183,7 +185,7 @@ void Label::_shape() const {
ERR_FAIL_COND(font.is_null());
if (para.dirty) {
TS->shaped_text_add_string(para.text_rid, para.text, font->get_rids(), font_size, font->get_opentype_features(), language);
TS->shaped_text_add_string(para.text_rid, para.text, font->get_rids(), font_size, font->get_opentype_features(), lang);
} else {
int spans = TS->shaped_get_span_count(para.text_rid);
for (int i = 0; i < spans; i++) {
@ -709,15 +711,14 @@ void Label::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSLATION_CHANGED: {
String new_text = atr(text);
if (new_text == xl_text) {
return; // Nothing new.
const String new_text = atr(text);
if (new_text != xl_text) {
xl_text = new_text;
if (visible_ratio < 1) {
visible_chars = get_total_character_count() * visible_ratio;
}
}
xl_text = new_text;
if (visible_ratio < 1) {
visible_chars = get_total_character_count() * visible_ratio;
}
text_dirty = true;
text_dirty = true; // Language update might change the appearance of some characters.
queue_accessibility_update();
queue_redraw();

View file

@ -1202,11 +1202,8 @@ void LineEdit::_notification(int p_what) {
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_REPLACE_SELECTED_TEXT, callable_mp(this, &LineEdit::_accessibility_action_replace_selected));
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SET_VALUE, callable_mp(this, &LineEdit::_accessibility_action_set_value));
DisplayServer::get_singleton()->accessibility_update_add_action(ae, DisplayServer::AccessibilityAction::ACTION_SHOW_CONTEXT_MENU, callable_mp(this, &LineEdit::_accessibility_action_menu));
if (!language.is_empty()) {
DisplayServer::get_singleton()->accessibility_update_set_language(ae, language);
} else {
DisplayServer::get_singleton()->accessibility_update_set_language(ae, TranslationServer::get_singleton()->get_tool_locale());
}
const String &lang = language.is_empty() ? _get_locale() : language;
DisplayServer::get_singleton()->accessibility_update_set_language(ae, lang);
bool rtl = is_layout_rtl();
Ref<StyleBox> style = theme_cache.normal;
@ -2979,7 +2976,8 @@ void LineEdit::_shape() {
}
TS->shaped_text_set_preserve_control(text_rid, draw_control_chars);
TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, font->get_opentype_features(), language);
const String &lang = language.is_empty() ? _get_locale() : language;
TS->shaped_text_add_string(text_rid, t, font->get_rids(), font_size, font->get_opentype_features(), lang);
TS->shaped_text_set_bidi_override(text_rid, structured_text_parser(st_parser, st_args, t));
full_width = TS->shaped_text_get_size(text_rid).x;

View file

@ -43,7 +43,8 @@ void LinkButton::_shape() {
text_buf->set_direction((TextServer::Direction)text_direction);
}
TS->shaped_text_set_bidi_override(text_buf->get_rid(), structured_text_parser(st_parser, st_args, xl_text));
text_buf->add_string(xl_text, font, font_size, language);
const String &lang = language.is_empty() ? _get_locale() : language;
text_buf->add_string(xl_text, font, font_size, lang);
queue_accessibility_update();
}

View file

@ -517,7 +517,8 @@ void MenuBar::shape(Menu &p_menu) {
} else {
p_menu.text_buf->set_direction((TextServer::Direction)text_direction);
}
p_menu.text_buf->add_string(atr(p_menu.name), theme_cache.font, theme_cache.font_size, language);
const String &lang = language.is_empty() ? _get_locale() : language;
p_menu.text_buf->add_string(atr(p_menu.name), theme_cache.font, theme_cache.font_size, lang);
}
void MenuBar::_refresh_menu_names() {

View file

@ -1018,11 +1018,12 @@ void PopupMenu::_shape_item(int p_idx) const {
} else {
items.write[p_idx].text_buf->set_direction((TextServer::Direction)items[p_idx].text_direction);
}
items.write[p_idx].text_buf->add_string(items.write[p_idx].xl_text, font, font_size, items[p_idx].language);
const String &lang = items[p_idx].language.is_empty() ? _get_locale() : items[p_idx].language;
items.write[p_idx].text_buf->add_string(items.write[p_idx].xl_text, font, font_size, lang);
items.write[p_idx].accel_text_buf->clear();
items.write[p_idx].accel_text_buf->set_direction(is_layout_rtl() ? TextServer::DIRECTION_RTL : TextServer::DIRECTION_LTR);
items.write[p_idx].accel_text_buf->add_string(_get_accel_text(items.write[p_idx]), font, font_size);
items.write[p_idx].accel_text_buf->add_string(_get_accel_text(items.write[p_idx]), font, font_size, lang);
items.write[p_idx].dirty = false;
}
}

View file

@ -167,7 +167,8 @@ void ProgressBar::_notification(int p_what) {
String txt = itos(int(ratio * 100));
if (is_localizing_numeral_system()) {
txt = TS->format_number(txt) + TS->percent_sign();
const String &lang = _get_locale();
txt = TS->format_number(txt, lang) + TS->percent_sign(lang);
} else {
txt += String("%");
}

View file

@ -3461,21 +3461,20 @@ TextServer::StructuredTextParser RichTextLabel::_find_stt(Item *p_item) {
}
String RichTextLabel::_find_language(Item *p_item) {
Item *item = p_item;
while (item) {
String lang = language;
for (Item *item = p_item; item; item = item->parent) {
if (item->type == ITEM_LANGUAGE) {
ItemLanguage *p = static_cast<ItemLanguage *>(item);
return p->language;
} else if (item->type == ITEM_PARAGRAPH) {
ItemParagraph *p = static_cast<ItemParagraph *>(item);
return p->language;
lang = p->language;
break;
}
if (item->type == ITEM_PARAGRAPH) {
ItemParagraph *p = static_cast<ItemParagraph *>(item);
lang = p->language;
break;
}
item = item->parent;
}
return language;
return lang.is_empty() ? _get_locale() : lang;
}
Color RichTextLabel::_find_color(Item *p_item, const Color &p_default_color) {

View file

@ -88,7 +88,7 @@ void SpinBox::_update_text(bool p_only_update_if_value_changed) {
double step = get_step();
String value = String::num(get_value(), Math::range_step_decimals(step));
if (is_localizing_numeral_system()) {
value = TS->format_number(value);
value = TS->format_number(value, _get_locale());
}
if (p_only_update_if_value_changed && value == last_text_value) {
@ -137,8 +137,9 @@ void SpinBox::_text_submitted(const String &p_string) {
Ref<Expression> expr;
expr.instantiate();
const String &lang = _get_locale();
text = text.replace_char(';', ',');
text = TS->parse_number(text);
text = TS->parse_number(text, lang);
// Ignore the prefix and suffix in the expression.
text = text.trim_prefix(prefix + " ").trim_suffix(" " + suffix);
@ -147,7 +148,7 @@ void SpinBox::_text_submitted(const String &p_string) {
if (err != OK) {
// If the expression failed try without converting commas to dots - they might have been for parameter separation.
text = p_string;
text = TS->parse_number(text);
text = TS->parse_number(text, lang);
text = text.trim_prefix(prefix + " ").trim_suffix(" " + suffix);
err = expr->parse(text);

View file

@ -363,7 +363,8 @@ void TabBar::_shape(int p_tab) {
tabs.write[p_tab].text_buf->set_direction((TextServer::Direction)tabs[p_tab].text_direction);
}
tabs.write[p_tab].text_buf->add_string(atr(tabs[p_tab].text), theme_cache.font, theme_cache.font_size, tabs[p_tab].language);
const String &lang = tabs[p_tab].language.is_empty() ? _get_locale() : tabs[p_tab].language;
tabs.write[p_tab].text_buf->add_string(atr(tabs[p_tab].text), theme_cache.font, theme_cache.font_size, lang);
}
RID TabBar::get_tab_accessibility_element(int p_tab) const {

View file

@ -39,7 +39,6 @@
#include "core/os/os.h"
#include "core/string/alt_codes.h"
#include "core/string/string_builder.h"
#include "core/string/translation_server.h"
#include "scene/gui/label.h"
#include "scene/main/window.h"
#include "scene/theme/theme_db.h"
@ -863,7 +862,7 @@ void TextEdit::_notification(int p_what) {
case NOTIFICATION_TRANSLATION_CHANGED:
case NOTIFICATION_THEME_CHANGED: {
if (is_inside_tree()) {
_update_caches();
_update_caches(p_what == NOTIFICATION_TRANSLATION_CHANGED);
_update_wrap_at_column(true);
}
} break;
@ -1435,7 +1434,7 @@ void TextEdit::_notification(int p_what) {
Ref<TextLine> tl;
tl.instantiate();
tl->add_string(txt, theme_cache.font, theme_cache.font_size);
tl->add_string(txt, theme_cache.font, theme_cache.font_size, _get_locale());
int yofs = ofs_y + (row_height - tl->get_size().y) / 2;
if (theme_cache.outline_size > 0 && theme_cache.outline_color.a > 0) {
@ -3356,7 +3355,8 @@ void TextEdit::_update_placeholder() {
placeholder_data_buf->set_direction((TextServer::Direction)text_direction);
}
placeholder_data_buf->set_preserve_control(draw_control_chars);
placeholder_data_buf->add_string(placeholder_translated, theme_cache.font, theme_cache.font_size, language);
const String &lang = language.is_empty() ? _get_locale() : language;
placeholder_data_buf->add_string(placeholder_translated, theme_cache.font, theme_cache.font_size, lang);
placeholder_bidi_override = structured_text_parser(st_parser, st_args, placeholder_translated);
if (placeholder_bidi_override.is_empty()) {
@ -3402,7 +3402,7 @@ void TextEdit::_update_theme_item_cache() {
}
}
void TextEdit::_update_caches() {
void TextEdit::_update_caches(bool p_invalidate_all) {
/* Text properties. */
TextServer::Direction dir;
if (text_direction == Control::TEXT_DIRECTION_INHERITED) {
@ -3410,11 +3410,16 @@ void TextEdit::_update_caches() {
} else {
dir = (TextServer::Direction)text_direction;
}
text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
const String &lang = language.is_empty() ? _get_locale() : language;
text.set_direction_and_language(dir, lang);
text.set_draw_control_chars(draw_control_chars);
text.set_font(theme_cache.font);
text.set_font_size(theme_cache.font_size);
text.invalidate_font();
if (p_invalidate_all) {
text.invalidate_all();
} else {
text.invalidate_font();
}
_update_placeholder();
/* Syntax highlighting. */
@ -3741,8 +3746,9 @@ void TextEdit::set_text_direction(Control::TextDirection p_text_direction) {
} else {
dir = (TextServer::Direction)text_direction;
}
text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
text.invalidate_font();
const String &lang = language.is_empty() ? _get_locale() : language;
text.set_direction_and_language(dir, lang);
text.invalidate_all();
_update_placeholder();
if (menu_dir) {
@ -3769,7 +3775,8 @@ void TextEdit::set_language(const String &p_language) {
} else {
dir = (TextServer::Direction)text_direction;
}
text.set_direction_and_language(dir, (!language.is_empty()) ? language : TranslationServer::get_singleton()->get_tool_locale());
const String &lang = language.is_empty() ? _get_locale() : language;
text.set_direction_and_language(dir, lang);
text.invalidate_all();
_update_placeholder();
queue_accessibility_update();

View file

@ -333,7 +333,7 @@ private:
Array st_args;
void _clear();
void _update_caches();
void _update_caches(bool p_invalidate_all = false);
void _close_ime_window();
void _update_ime_window_position();

View file

@ -2131,7 +2131,8 @@ void Tree::update_column(int p_col) {
}
columns.write[p_col].xl_title = atr(columns[p_col].title);
columns.write[p_col].text_buf->add_string(columns[p_col].xl_title, theme_cache.tb_font, theme_cache.tb_font_size, columns[p_col].language);
const String &lang = columns[p_col].language.is_empty() ? _get_locale() : columns[p_col].language;
columns.write[p_col].text_buf->add_string(columns[p_col].xl_title, theme_cache.tb_font, theme_cache.tb_font_size, lang);
columns.write[p_col].cached_minimum_width_dirty = true;
}
@ -2200,7 +2201,8 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) const {
} else {
font_size = theme_cache.font_size;
}
p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, p_item->cells[p_col].language);
const String &lang = p_item->cells[p_col].language.is_empty() ? _get_locale() : p_item->cells[p_col].language;
p_item->cells.write[p_col].text_buf->add_string(valtext, font, font_size, lang);
BitField<TextServer::LineBreakFlag> break_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES;
switch (p_item->cells.write[p_col].autowrap_mode) {

View file

@ -2779,27 +2779,6 @@ void Node::get_storable_properties(HashSet<StringName> &r_storable_properties) c
}
}
String Node::to_string() {
// Keep this method in sync with `Object::to_string`.
ERR_THREAD_GUARD_V(String());
if (get_script_instance()) {
bool valid;
String ret = get_script_instance()->to_string(&valid);
if (valid) {
return ret;
}
}
if (_get_extension() && _get_extension()->to_string) {
String ret;
GDExtensionBool is_valid;
_get_extension()->to_string(_get_extension_instance(), &is_valid, &ret);
if (is_valid) {
return ret;
}
}
return (get_name() ? String(get_name()) + ":" : "") + Object::to_string();
}
void Node::set_scene_instance_state(const Ref<SceneState> &p_state) {
ERR_THREAD_GUARD
data.instance_state = p_state;
@ -3615,6 +3594,11 @@ void Node::_validate_property(PropertyInfo &p_property) const {
}
}
String Node::_to_string() {
ERR_THREAD_GUARD_V(String());
return (get_name() ? String(get_name()) + ":" : "") + Object::_to_string();
}
void Node::input(const Ref<InputEvent> &p_event) {
}

View file

@ -407,6 +407,7 @@ protected:
void _call_unhandled_key_input(const Ref<InputEvent> &p_event);
void _validate_property(PropertyInfo &p_property) const;
virtual String _to_string() override;
Variant _get_node_rpc_config_bind() const {
return get_node_rpc_config().duplicate(true);
@ -631,8 +632,6 @@ public:
#endif
void get_storable_properties(HashSet<StringName> &r_storable_properties) const;
virtual String to_string() override;
/* NOTIFICATIONS */
void propagate_notification(int p_notification);

View file

@ -32,7 +32,6 @@
#include "core/config/project_settings.h"
#include "core/debugger/engine_debugger.h"
#include "core/string/translation_server.h"
#include "scene/gui/control.h"
#include "scene/theme/theme_db.h"
#include "scene/theme/theme_owner.h"
@ -2934,12 +2933,9 @@ bool Window::is_layout_rtl() const {
} else if (proj_root_layout_direction == 2) {
return true;
} else if (proj_root_layout_direction == 3) {
String locale = OS::get_singleton()->get_locale();
return TS->is_locale_right_to_left(locale);
return TS->is_locale_right_to_left(OS::get_singleton()->get_locale());
} else {
const Ref<Translation> &t = TranslationServer::get_singleton()->get_translation_object(TranslationServer::get_singleton()->get_locale());
String locale = t.is_valid() ? t->get_locale() : TranslationServer::get_singleton()->get_fallback_locale();
return TS->is_locale_right_to_left(locale);
return TS->is_locale_right_to_left(_get_locale());
}
}
}
@ -2948,8 +2944,9 @@ bool Window::is_layout_rtl() const {
return true;
}
#endif
const StringName domain_name = get_translation_domain();
Node *parent_node = get_parent();
while (parent_node) {
while (parent_node && parent_node->get_translation_domain() == domain_name) {
Control *parent_control = Object::cast_to<Control>(parent_node);
if (parent_control) {
return parent_control->is_layout_rtl();
@ -2967,25 +2964,21 @@ bool Window::is_layout_rtl() const {
} else if (root_layout_direction == 2) {
return true;
} else if (root_layout_direction == 3) {
String locale = OS::get_singleton()->get_locale();
return TS->is_locale_right_to_left(locale);
return TS->is_locale_right_to_left(OS::get_singleton()->get_locale());
} else {
String locale = TranslationServer::get_singleton()->get_tool_locale();
return TS->is_locale_right_to_left(locale);
return TS->is_locale_right_to_left(_get_locale());
}
} else if (layout_dir == LAYOUT_DIRECTION_APPLICATION_LOCALE) {
if (GLOBAL_GET_CACHED(bool, "internationalization/rendering/force_right_to_left_layout_direction")) {
return true;
} else {
String locale = TranslationServer::get_singleton()->get_tool_locale();
return TS->is_locale_right_to_left(locale);
return TS->is_locale_right_to_left(_get_locale());
}
} else if (layout_dir == LAYOUT_DIRECTION_SYSTEM_LOCALE) {
if (GLOBAL_GET_CACHED(bool, "internationalization/rendering/force_right_to_left_layout_direction")) {
return true;
} else {
String locale = OS::get_singleton()->get_locale();
return TS->is_locale_right_to_left(locale);
return TS->is_locale_right_to_left(OS::get_singleton()->get_locale());
}
} else {
return (layout_dir == LAYOUT_DIRECTION_RTL);

View file

@ -3227,8 +3227,9 @@ void TextMesh::_create_mesh_array(Array &p_arr) const {
TS->shaped_text_clear(text_rid);
TS->shaped_text_set_direction(text_rid, text_direction);
String txt = (uppercase) ? TS->string_to_upper(xl_text, language) : xl_text;
TS->shaped_text_add_string(text_rid, txt, font->get_rids(), font_size, font->get_opentype_features(), language);
const String &lang = language.is_empty() ? _get_locale() : language;
String txt = (uppercase) ? TS->string_to_upper(xl_text, lang) : xl_text;
TS->shaped_text_add_string(text_rid, txt, font->get_rids(), font_size, font->get_opentype_features(), lang);
TypedArray<Vector3i> stt;
if (st_parser == TextServer::STRUCTURED_TEXT_CUSTOM) {
@ -3686,11 +3687,8 @@ void TextMesh::_bind_methods() {
void TextMesh::_notification(int p_what) {
switch (p_what) {
case MainLoop::NOTIFICATION_TRANSLATION_CHANGED: {
String new_text = tr(text);
if (new_text == xl_text) {
return; // Nothing new.
}
xl_text = new_text;
// Language update might change the appearance of some characters.
xl_text = tr(text);
dirty_text = true;
request_update();
} break;

View file

@ -6584,11 +6584,14 @@ void RenderingDevice::_stall_for_previous_frames() {
}
}
void RenderingDevice::_flush_and_stall_for_all_frames() {
void RenderingDevice::_flush_and_stall_for_all_frames(bool p_begin_frame) {
_stall_for_previous_frames();
_end_frame();
_execute_frame(false);
_begin_frame();
if (p_begin_frame) {
_begin_frame();
}
}
Error RenderingDevice::initialize(RenderingContextDriver *p_context, DisplayServer::WindowID p_main_window) {
@ -7086,7 +7089,7 @@ void RenderingDevice::finalize() {
if (!frames.is_empty()) {
// Wait for all frames to have finished rendering.
_flush_and_stall_for_all_frames();
_flush_and_stall_for_all_frames(false);
}
// Wait for transfer workers to finish.

View file

@ -1587,7 +1587,7 @@ public:
void _execute_frame(bool p_present);
void _stall_for_frame(uint32_t p_frame);
void _stall_for_previous_frames();
void _flush_and_stall_for_all_frames();
void _flush_and_stall_for_all_frames(bool p_begin_frame = true);
template <typename T>
void _free_rids(T &p_owner, const char *p_type);