From 7aeffb89230a882f85f12f4b00facb4628b9b3dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=CC=84vels=20Nadtoc=CC=8Cajevs?= <7645683+bruvzg@users.noreply.github.com> Date: Fri, 7 Feb 2025 08:25:44 +0200 Subject: [PATCH] [TextEdit] Improve wrapped line indent handling. --- scene/gui/text_edit.cpp | 66 +++++++++++++++++++++++++++++++++++++---- scene/gui/text_edit.h | 2 ++ servers/text_server.cpp | 45 +++++++++++++++++++--------- 3 files changed, 94 insertions(+), 19 deletions(-) diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index aa85b3d9cc8..08edb4facb4 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -886,6 +886,12 @@ void TextEdit::_notification(int p_what) { int line_wrap_amount = get_line_wrap_count(minimap_line); int last_wrap_column = 0; + int first_indent_line = 0; + float wrap_indent_line = 0.0; + if (text.is_indent_wrapped_lines()) { + wrap_indent_line = _get_wrapped_indent_level(minimap_line, first_indent_line); + wrap_indent_line = MIN(wrap_indent_line, (minimap_width / minimap_char_size.x) * 0.6); + } for (int line_wrap_index = 0; line_wrap_index < line_wrap_amount + 1; line_wrap_index++) { if (line_wrap_index != 0) { i++; @@ -895,7 +901,7 @@ void TextEdit::_notification(int p_what) { } const String &str = wrap_rows[line_wrap_index]; - int indent_px = line_wrap_index != 0 ? get_indent_level(minimap_line) : 0; + int indent_px = line_wrap_index > first_indent_line ? wrap_indent_line : 0.0; if (indent_px >= wrap_at_column) { indent_px = 0; } @@ -1043,6 +1049,12 @@ void TextEdit::_notification(int p_what) { const Vector wrap_rows = draw_placeholder ? placeholder_wrapped_rows : get_line_wrapped_text(line); int line_wrap_amount = draw_placeholder ? placeholder_wrapped_rows.size() - 1 : get_line_wrap_count(line); + int first_indent_line = 0; + float wrap_indent_line = 0.0; + if (text.is_indent_wrapped_lines()) { + wrap_indent_line = _get_wrapped_indent_level(line, first_indent_line) * theme_cache.font->get_char_size(' ', theme_cache.font_size).width; + wrap_indent_line = MIN(wrap_indent_line, wrap_at_column * 0.6); + } for (int line_wrap_index = 0; line_wrap_index <= line_wrap_amount; line_wrap_index++) { if (line_wrap_index != 0) { i++; @@ -1174,7 +1186,7 @@ void TextEdit::_notification(int p_what) { // Draw line. RID rid = ldata->get_line_rid(line_wrap_index); float text_height = TS->shaped_text_get_size(rid).y; - float wrap_indent = (text.is_indent_wrapped_lines() && line_wrap_index > 0) ? get_indent_level(line) * theme_cache.font->get_char_size(' ', theme_cache.font_size).width : 0.0; + float wrap_indent = line_wrap_index > first_indent_line ? wrap_indent_line : 0.0; if (rtl) { char_margin = size.width - char_margin - (TS->shaped_text_get_size(rid).x + wrap_indent); @@ -3529,6 +3541,32 @@ int TextEdit::get_line_height() const { return MAX(text.get_line_height() + theme_cache.line_spacing, 1); } +int TextEdit::_get_wrapped_indent_level(int p_line, int &r_first_wrap) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); + + const Vector wr = text.get_line_wrap_ranges(p_line); + r_first_wrap = 0; + + int tab_count = 0; + int whitespace_count = 0; + int line_length = text[p_line].size(); + for (int i = 0; i < line_length - 1; i++) { + if (r_first_wrap < wr.size() && i >= wr[r_first_wrap].y) { + tab_count = 0; + whitespace_count = 0; + r_first_wrap++; + } + if (text[p_line][i] == '\t') { + tab_count++; + } else if (text[p_line][i] == ' ') { + whitespace_count++; + } else { + break; + } + } + return tab_count * text.get_tab_size() + whitespace_count; +} + int TextEdit::get_indent_level(int p_line) const { ERR_FAIL_INDEX_V(p_line, text.size(), 0); @@ -4452,7 +4490,13 @@ Point2i TextEdit::get_line_column_at_pos(const Point2i &p_pos, bool p_clamp_line } RID text_rid = text.get_line_data(row)->get_line_rid(wrap_index); - float wrap_indent = (text.is_indent_wrapped_lines() && wrap_index > 0) ? get_indent_level(row) * theme_cache.font->get_char_size(' ', theme_cache.font_size).width : 0.0; + int first_indent_line = 0; + float wrap_indent_line = 0.0; + if (text.is_indent_wrapped_lines()) { + wrap_indent_line = _get_wrapped_indent_level(row, first_indent_line) * theme_cache.font->get_char_size(' ', theme_cache.font_size).width; + wrap_indent_line = MIN(wrap_indent_line, wrap_at_column * 0.6); + } + float wrap_indent = wrap_index > first_indent_line ? wrap_indent_line : 0.0; if (is_layout_rtl()) { colx = TS->shaped_text_get_size(text_rid).x - colx + wrap_indent; } else { @@ -7552,7 +7596,13 @@ int TextEdit::_get_char_pos_for_line(int p_px, int p_line, int p_wrap_index) con p_wrap_index = MIN(p_wrap_index, text.get_line_data(p_line)->get_line_count() - 1); RID text_rid = text.get_line_data(p_line)->get_line_rid(p_wrap_index); - float wrap_indent = (text.is_indent_wrapped_lines() && p_wrap_index > 0) ? get_indent_level(p_line) * theme_cache.font->get_char_size(' ', theme_cache.font_size).width : 0.0; + int first_indent_line = 0; + float wrap_indent_line = 0.0; + if (text.is_indent_wrapped_lines()) { + wrap_indent_line = _get_wrapped_indent_level(p_line, first_indent_line) * theme_cache.font->get_char_size(' ', theme_cache.font_size).width; + wrap_indent_line = MIN(wrap_indent_line, wrap_at_column * 0.6); + } + float wrap_indent = p_wrap_index > first_indent_line ? wrap_indent_line : 0.0; if (is_layout_rtl()) { p_px = TS->shaped_text_get_size(text_rid).x - p_px + wrap_indent; } else { @@ -7621,7 +7671,13 @@ int TextEdit::_get_column_x_offset_for_line(int p_char, int p_line, int p_column } RID text_rid = text.get_line_data(p_line)->get_line_rid(row); - float wrap_indent = (text.is_indent_wrapped_lines() && row > 0) ? get_indent_level(p_line) * theme_cache.font->get_char_size(' ', theme_cache.font_size).width : 0.0; + int first_indent_line = 0; + float wrap_indent_line = 0.0; + if (text.is_indent_wrapped_lines()) { + wrap_indent_line = _get_wrapped_indent_level(p_line, first_indent_line) * theme_cache.font->get_char_size(' ', theme_cache.font_size).width; + wrap_indent_line = MIN(wrap_indent_line, wrap_at_column * 0.6); + } + float wrap_indent = row > first_indent_line ? wrap_indent_line : 0.0; CaretInfo ts_caret = TS->shaped_text_get_carets(text_rid, p_column); if ((ts_caret.l_caret != Rect2() && (ts_caret.l_dir == TextServer::DIRECTION_AUTO || ts_caret.l_dir == (TextServer::Direction)input_direction)) || (ts_caret.t_caret == Rect2())) { return ts_caret.l_caret.position.x + (is_layout_rtl() ? -wrap_indent : wrap_indent); diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 44168a8a268..efb7825c890 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -690,6 +690,8 @@ protected: void _unhide_all_lines(); virtual void _unhide_carets(); + int _get_wrapped_indent_level(int p_line, int &r_first_wrap) const; + // Symbol lookup. String lookup_symbol_word; void _set_symbol_lookup_word(const String &p_symbol); diff --git a/servers/text_server.cpp b/servers/text_server.cpp index 83a53bfc7c1..8945f875fce 100644 --- a/servers/text_server.cpp +++ b/servers/text_server.cpp @@ -803,25 +803,36 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks_adv(const RID &p_shaped int last_safe_break = -1; int word_count = 0; int chunk = 0; + int prev_chunk = -1; bool trim_next = false; int l_size = shaped_text_get_glyph_count(p_shaped); const Glyph *l_gl = const_cast(this)->shaped_text_sort_logical(p_shaped); + int indent_end = 0; double indent = 0.0; - if (p_break_flags.has_flag(BREAK_TRIM_INDENT)) { - for (int i = 0; i < l_size; i++) { - if ((l_gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB || (l_gl[i].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) { - indent += l_gl[i].advance * l_gl[i].repeat; - } else { - break; - } - } - } for (int i = 0; i < l_size; i++) { double l_width = p_width[chunk]; - if (l_width > indent) { + + if (p_break_flags.has_flag(BREAK_TRIM_INDENT) && chunk != prev_chunk) { + indent = 0.0; + for (int j = indent_end; j < l_size; j++) { + if ((l_gl[j].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB || (l_gl[j].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) { + if (indent + l_gl[j].advance * l_gl[j].repeat > l_width) { + indent = 0.0; + } + indent += l_gl[j].advance * l_gl[j].repeat; + indent_end = l_gl[j].end; + } else { + break; + } + } + indent = MIN(indent, 0.6 * l_width); + prev_chunk = chunk; + } + + if (l_width > indent && i > indent_end) { l_width -= indent; } if (l_gl[i].start < p_start) { @@ -982,15 +993,21 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do int l_size = shaped_text_get_glyph_count(p_shaped); const Glyph *l_gl = const_cast(this)->shaped_text_sort_logical(p_shaped); + int indent_end = 0; double indent = 0.0; if (p_break_flags.has_flag(BREAK_TRIM_INDENT)) { for (int i = 0; i < l_size; i++) { if ((l_gl[i].flags & GRAPHEME_IS_TAB) == GRAPHEME_IS_TAB || (l_gl[i].flags & GRAPHEME_IS_SPACE) == GRAPHEME_IS_SPACE) { + if (indent + l_gl[i].advance * l_gl[i].repeat > p_width) { + indent = 0.0; + } indent += l_gl[i].advance * l_gl[i].repeat; + indent_end = l_gl[i].end; } else { break; } } + indent = MIN(indent, 0.6 * p_width); } double l_width = p_width; @@ -1018,7 +1035,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do if (last_end <= l_gl[start_pos].start) { lines.push_back(l_gl[start_pos].start); lines.push_back(l_gl[end_pos].end); - if (p_width > indent) { + if (p_width > indent && i > indent_end) { l_width = p_width - indent; } cur_safe_brk = end_pos; @@ -1029,7 +1046,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do if (last_end <= line_start) { lines.push_back(line_start); lines.push_back(l_gl[last_safe_break].end); - if (p_width > indent) { + if (p_width > indent && i > indent_end) { l_width = p_width - indent; } last_end = l_gl[last_safe_break].end; @@ -1062,7 +1079,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do if (last_end <= l_gl[start_pos].start) { lines.push_back(l_gl[start_pos].start); lines.push_back(l_gl[end_pos].end); - if (p_width > indent) { + if (p_width > indent && i > indent_end) { l_width = p_width - indent; } last_end = l_gl[i].end; @@ -1072,7 +1089,7 @@ PackedInt32Array TextServer::shaped_text_get_line_breaks(const RID &p_shaped, do if (last_end <= line_start) { lines.push_back(line_start); lines.push_back(l_gl[i].end); - if (p_width > indent) { + if (p_width > indent && i > indent_end) { l_width = p_width - indent; } last_end = l_gl[i].end;