From e046830c5037ea3c06e3c8cc9211170dbb11bad2 Mon Sep 17 00:00:00 2001 From: Philippe Vaillancourt Date: Wed, 24 Dec 2025 15:28:15 -0500 Subject: [PATCH] Tree: Add per-cell autowrap_trim_flags to TreeItem The Debugger Errors tab previously displayed ASCII art diagnostic output (like Rust/Elm-style error messages) incorrectly because the Tree widget hardcoded space trimming flags, causing leading spaces on wrapped lines to be trimmed and breaking alignment. This adds a per-cell `autowrap_trim_flags` property to TreeItem, following the pattern used by Label and RichTextLabel. The debugger errors tab now disables trim flags and uses the same monospace font as the Output panel, ensuring proper alignment of structured error messages. --- doc/classes/TreeItem.xml | 15 +++++++++++++ editor/debugger/script_editor_debugger.cpp | 8 +++++++ scene/gui/tree.cpp | 25 +++++++++++++++++++++- scene/gui/tree.h | 4 ++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/doc/classes/TreeItem.xml b/doc/classes/TreeItem.xml index f0c20fae428..31a16ccffd4 100644 --- a/doc/classes/TreeItem.xml +++ b/doc/classes/TreeItem.xml @@ -94,6 +94,13 @@ Returns the text autowrap mode in the given [param column]. By default it is [constant TextServer.AUTOWRAP_OFF]. + + + + + Returns the autowrap trim flags for the given [param column]. By default, both [constant TextServer.BREAK_TRIM_START_EDGE_SPACES] and [constant TextServer.BREAK_TRIM_END_EDGE_SPACES] are enabled. + + @@ -538,6 +545,14 @@ Sets the autowrap mode in the given [param column]. If set to something other than [constant TextServer.AUTOWRAP_OFF], the text gets wrapped inside the cell's bounding rectangle. + + + + + + Sets the autowrap trim flags for the given [param column]. These flags control whether leading and trailing spaces are trimmed on wrapped lines. Set to [code]0[/code] to disable all trimming. + + diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index b7376772038..92c0e63efaf 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -678,6 +678,7 @@ void ScriptEditorDebugger::_msg_error(uint64_t p_thread_id, const Array &p_data) error_title += oe.error_descr.is_empty() ? oe.error : oe.error_descr; error->set_text(1, error_title); error->set_autowrap_mode(1, TextServer::AUTOWRAP_WORD_SMART); + error->set_autowrap_trim_flags(1, 0); tooltip += " " + error_title + "\n"; // Find the language of the error's source file. @@ -1117,6 +1118,13 @@ void ScriptEditorDebugger::_notification(int p_what) { reason->add_theme_color_override(SNAME("default_color"), get_theme_color(SNAME("error_color"), EditorStringName(Editor))); reason->add_theme_style_override(SNAME("normal"), get_theme_stylebox(SNAME("normal"), SNAME("Label"))); // Empty stylebox. + const Ref source_font = get_theme_font(SNAME("output_source"), EditorStringName(EditorFonts)); + if (source_font.is_valid()) { + error_tree->add_theme_font_override("font", source_font); + } + const int font_size = get_theme_font_size(SNAME("output_source_size"), EditorStringName(EditorFonts)); + error_tree->add_theme_font_size_override("font_size", font_size); + TreeItem *error_root = error_tree->get_root(); if (error_root) { TreeItem *error = error_root->get_first_child(); diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index a8db87aeb18..7685bf5aff4 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -460,6 +460,26 @@ TextServer::AutowrapMode TreeItem::get_autowrap_mode(int p_column) const { return cells[p_column].autowrap_mode; } +void TreeItem::set_autowrap_trim_flags(int p_column, BitField p_flags) { + ERR_FAIL_INDEX(p_column, cells.size()); + + // Only trim-related flags are valid for this property. + BitField masked_flags = p_flags & TextServer::BREAK_TRIM_MASK; + if (cells[p_column].autowrap_trim_flags == masked_flags) { + return; + } + + cells.write[p_column].autowrap_trim_flags = masked_flags; + cells.write[p_column].dirty = true; + _changed_notify(p_column); + cells.write[p_column].cached_minimum_size_dirty = true; +} + +BitField TreeItem::get_autowrap_trim_flags(int p_column) const { + ERR_FAIL_INDEX_V(p_column, cells.size(), TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES); + return cells[p_column].autowrap_trim_flags; +} + void TreeItem::set_text_overrun_behavior(int p_column, TextServer::OverrunBehavior p_behavior) { ERR_FAIL_INDEX(p_column, cells.size()); @@ -1810,6 +1830,9 @@ void TreeItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_autowrap_mode", "column", "autowrap_mode"), &TreeItem::set_autowrap_mode); ClassDB::bind_method(D_METHOD("get_autowrap_mode", "column"), &TreeItem::get_autowrap_mode); + ClassDB::bind_method(D_METHOD("set_autowrap_trim_flags", "column", "flags"), &TreeItem::set_autowrap_trim_flags); + ClassDB::bind_method(D_METHOD("get_autowrap_trim_flags", "column"), &TreeItem::get_autowrap_trim_flags); + ClassDB::bind_method(D_METHOD("set_text_overrun_behavior", "column", "overrun_behavior"), &TreeItem::set_text_overrun_behavior); ClassDB::bind_method(D_METHOD("get_text_overrun_behavior", "column"), &TreeItem::get_text_overrun_behavior); @@ -2218,7 +2241,7 @@ void Tree::update_item_cell(TreeItem *p_item, int p_col) const { 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 break_flags = TextServer::BREAK_MANDATORY | TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES; + BitField break_flags = TextServer::BREAK_MANDATORY | p_item->cells[p_col].autowrap_trim_flags; switch (p_item->cells.write[p_col].autowrap_mode) { case TextServer::AUTOWRAP_OFF: break; diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 301a98263ad..6a335366690 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -78,6 +78,7 @@ private: Array st_args; Control::TextDirection text_direction = Control::TEXT_DIRECTION_INHERITED; TextServer::AutowrapMode autowrap_mode = TextServer::AUTOWRAP_OFF; + BitField autowrap_trim_flags = TextServer::BREAK_TRIM_START_EDGE_SPACES | TextServer::BREAK_TRIM_END_EDGE_SPACES; bool dirty = true; double min = 0.0; double max = 100.0; @@ -284,6 +285,9 @@ public: void set_autowrap_mode(int p_column, TextServer::AutowrapMode p_mode); TextServer::AutowrapMode get_autowrap_mode(int p_column) const; + void set_autowrap_trim_flags(int p_column, BitField p_flags); + BitField get_autowrap_trim_flags(int p_column) const; + void set_text_overrun_behavior(int p_column, TextServer::OverrunBehavior p_behavior); TextServer::OverrunBehavior get_text_overrun_behavior(int p_column) const;