diff --git a/core/string/string_builder.cpp b/core/string/string_builder.cpp index 4f74a2e42dc7..d08bcdb9d481 100644 --- a/core/string/string_builder.cpp +++ b/core/string/string_builder.cpp @@ -56,24 +56,33 @@ StringBuilder &StringBuilder::append(const char *p_cstring) { return *this; } +void StringBuilder::clear() { + string_length = 0; + strings.clear(); + c_strings.clear(); + appended_strings.clear(); +} + String StringBuilder::as_string() const { if (string_length == 0) { return ""; } - char32_t *buffer = memnew_arr(char32_t, string_length); + String buffer; + buffer.resize(string_length + 1); + char32_t *buffer_ptr = buffer.ptrw(); int current_position = 0; int godot_string_elem = 0; int c_string_elem = 0; - for (int i = 0; i < appended_strings.size(); i++) { + for (uint32_t i = 0; i < appended_strings.size(); i++) { if (appended_strings[i] == -1) { // Godot string const String &s = strings[godot_string_elem]; - memcpy(buffer + current_position, s.ptr(), s.length() * sizeof(char32_t)); + memcpy(buffer_ptr + current_position, s.ptr(), s.length() * sizeof(char32_t)); current_position += s.length(); @@ -82,7 +91,7 @@ String StringBuilder::as_string() const { const char *s = c_strings[c_string_elem]; for (int32_t j = 0; j < appended_strings[i]; j++) { - buffer[current_position + j] = s[j]; + buffer_ptr[current_position + j] = s[j]; } current_position += appended_strings[i]; @@ -91,9 +100,7 @@ String StringBuilder::as_string() const { } } - String final_string = String(buffer, string_length); - - memdelete_arr(buffer); + buffer_ptr[current_position] = 0; - return final_string; + return buffer; } diff --git a/core/string/string_builder.h b/core/string/string_builder.h index 1a989e8422cf..5b50ead81df4 100644 --- a/core/string/string_builder.h +++ b/core/string/string_builder.h @@ -32,21 +32,22 @@ #define STRING_BUILDER_H #include "core/string/ustring.h" -#include "core/templates/vector.h" +#include "core/templates/local_vector.h" class StringBuilder { uint32_t string_length = 0; - Vector strings; - Vector c_strings; + LocalVector strings; + LocalVector c_strings; // -1 means it's a Godot String // a natural number means C string. - Vector appended_strings; + LocalVector appended_strings; public: StringBuilder &append(const String &p_string); StringBuilder &append(const char *p_cstring); + void clear(); _FORCE_INLINE_ StringBuilder &operator+(const String &p_string) { return append(p_string); diff --git a/editor/gui/scene_tree_editor.cpp b/editor/gui/scene_tree_editor.cpp index e89912d5bc68..4adab1c43c6e 100644 --- a/editor/gui/scene_tree_editor.cpp +++ b/editor/gui/scene_tree_editor.cpp @@ -32,6 +32,7 @@ #include "core/config/project_settings.h" #include "core/object/script_language.h" +#include "core/string/string_builder.h" #include "editor/editor_dock_manager.h" #include "editor/editor_file_system.h" #include "editor/editor_node.h" @@ -511,24 +512,36 @@ void SceneTreeEditor::_queue_update_node_tooltip(Node *p_node, TreeItem *p_item) } void SceneTreeEditor::_update_node_tooltip(Node *p_node, TreeItem *p_item) { + // Use static StringBuilder to reuse inner buffer, avoiding memory allocation. + static StringBuilder tooltip; + tooltip.clear(); // Display the node name in all tooltips so that long node names can be previewed // without having to rename them. - String tooltip = p_node->get_name(); + tooltip += p_node->get_name(); if (p_node == get_scene_node() && p_node->get_scene_inherited_state().is_valid()) { if (p_item->get_button_by_id(0, BUTTON_SUBSCENE) == -1) { p_item->add_button(0, get_editor_theme_icon(SNAME("InstanceOptions")), BUTTON_SUBSCENE, false, TTR("Open in Editor")); } - tooltip += String("\n" + TTR("Inherits:") + " " + p_node->get_scene_inherited_state()->get_path()); + tooltip += "\n"; + tooltip += TTR("Inherits:"); + tooltip += " "; + tooltip += p_node->get_scene_inherited_state()->get_path(); } else if (p_node != get_scene_node() && !p_node->get_scene_file_path().is_empty() && can_open_instance) { if (p_item->get_button_by_id(0, BUTTON_SUBSCENE) == -1) { p_item->add_button(0, get_editor_theme_icon(SNAME("InstanceOptions")), BUTTON_SUBSCENE, false, TTR("Open in Editor")); } - tooltip += String("\n" + TTR("Instance:") + " " + p_node->get_scene_file_path()); + tooltip += "\n"; + tooltip += TTR("Instance:"); + tooltip += " "; + tooltip += p_node->get_scene_file_path(); } StringName custom_type = EditorNode::get_singleton()->get_object_custom_type_name(p_node); - tooltip += "\n" + TTR("Type:") + " " + (custom_type != StringName() ? String(custom_type) : p_node->get_class()); + tooltip += "\n"; + tooltip += TTR("Type:"); + tooltip += " "; + tooltip += custom_type != StringName() ? String(custom_type) : p_node->get_class(); if (!p_node->get_editor_description().is_empty()) { const PackedInt32Array boundaries = TS->string_get_word_breaks(p_node->get_editor_description(), "", 80); @@ -537,11 +550,12 @@ void SceneTreeEditor::_update_node_tooltip(Node *p_node, TreeItem *p_item) { for (int i = 0; i < boundaries.size(); i += 2) { const int start = boundaries[i]; const int end = boundaries[i + 1]; - tooltip += "\n" + p_node->get_editor_description().substr(start, end - start + 1).rstrip("\n"); + tooltip += "\n"; + tooltip += p_node->get_editor_description().substr(start, end - start + 1).rstrip("\n"); } } - p_item->set_tooltip_text(0, tooltip); + p_item->set_tooltip_text(0, tooltip.as_string()); } void SceneTreeEditor::_node_visibility_changed(Node *p_node) {