From 2fb5059c63a5faa01780d918e5e40be74665d201 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pa=CC=84vels=20Nadtoc=CC=8Cajevs?= <7645683+bruvzg@users.noreply.github.com> Date: Mon, 20 Jan 2025 12:53:43 +0200 Subject: [PATCH] [TextEdit] Use actual indentation offset instead of space width for wrapped lines. --- scene/gui/text_edit.cpp | 65 +++++++++++++++++++++++++----------- scene/gui/text_edit.h | 2 ++ tests/scene/test_text_edit.h | 6 ++-- 3 files changed, 51 insertions(+), 22 deletions(-) diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 8501c5dbbb7..ac68acabb54 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -181,6 +181,36 @@ const Ref TextEdit::Text::get_line_data(int p_line) const { return text[p_line].data_buf; } +float TextEdit::Text::get_indent_offset(int p_line, bool p_rtl) const { + ERR_FAIL_INDEX_V(p_line, text.size(), 0); + Line &text_line = text.write[p_line]; + if (text_line.indent_ofs < 0.0) { + int char_count = 0; + int line_length = text_line.data.size(); + for (int i = 0; i < line_length - 1; i++) { + if (text_line.data[i] == '\t') { + char_count++; + } else if (text_line.data[i] == ' ') { + char_count++; + } else { + break; + } + } + RID text_rid = text_line.data_buf->get_line_rid(0); + float offset = (p_rtl) ? TS->shaped_text_get_size(text_rid).x : 0; + Vector sel = TS->shaped_text_get_selection(text_rid, 0, char_count); + for (const Vector2 v : sel) { + if (p_rtl) { + offset = MIN(v.x, MIN(v.y, offset)); + } else { + offset = MAX(v.x, MAX(v.y, offset)); + } + } + text_line.indent_ofs = (p_rtl) ? TS->shaped_text_get_size(text_rid).x - offset : offset; + } + return text_line.indent_ofs; +} + _FORCE_INLINE_ String TextEdit::Text::operator[](int p_line) const { ERR_FAIL_INDEX_V(p_line, text.size(), ""); return text[p_line].data; @@ -216,6 +246,7 @@ void TextEdit::Text::invalidate_cache(int p_line, bool p_text_changed) { text_line.data_buf->set_break_flags(flags); text_line.data_buf->set_preserve_control(draw_control_chars); text_line.data_buf->set_custom_punctuation(get_enabled_word_separators()); + text_line.indent_ofs = -1.0; const String &text_with_ime = (!text_line.ime_data.is_empty()) ? text_line.ime_data : text_line.data; const Array &bidi_override_with_ime = (!text_line.ime_data.is_empty()) ? text_line.ime_bidi_override : text_line.bidi_override; @@ -1059,11 +1090,10 @@ void TextEdit::_notification(int p_what) { 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); + _get_wrapped_indent_level(line, first_indent_line); } + float indent_ofs = MIN(text.get_indent_offset(line, rtl), 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++; @@ -1195,7 +1225,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 = line_wrap_index > first_indent_line ? wrap_indent_line : 0.0; + float wrap_indent = line_wrap_index > first_indent_line ? indent_ofs : 0.0; if (rtl) { char_margin = size.width - char_margin - (TS->shaped_text_get_size(rid).x + wrap_indent); @@ -4526,14 +4556,14 @@ 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); + + bool rtl = is_layout_rtl(); 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); + _get_wrapped_indent_level(row, first_indent_line); } - float wrap_indent = wrap_index > first_indent_line ? wrap_indent_line : 0.0; - if (is_layout_rtl()) { + float wrap_indent = wrap_index > first_indent_line ? MIN(text.get_indent_offset(row, rtl), wrap_at_column * 0.6) : 0.0; + if (rtl) { colx = TS->shaped_text_get_size(text_rid).x - colx + wrap_indent; } else { colx -= wrap_indent; @@ -7634,12 +7664,10 @@ int TextEdit::_get_char_pos_for_line(int p_px, int p_line, int p_wrap_index) con RID text_rid = text.get_line_data(p_line)->get_line_rid(p_wrap_index); 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); + _get_wrapped_indent_level(p_line, first_indent_line); } - float wrap_indent = p_wrap_index > first_indent_line ? wrap_indent_line : 0.0; + float wrap_indent = p_wrap_index > first_indent_line ? MIN(text.get_indent_offset(p_line, is_layout_rtl()), wrap_at_column * 0.6) : 0.0; if (is_layout_rtl()) { p_px = TS->shaped_text_get_size(text_rid).x - p_px + wrap_indent; } else { @@ -7708,18 +7736,17 @@ 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); + bool rtl = is_layout_rtl(); 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); + _get_wrapped_indent_level(p_line, first_indent_line); } - float wrap_indent = row > first_indent_line ? wrap_indent_line : 0.0; + float wrap_indent = row > first_indent_line ? MIN(text.get_indent_offset(p_line, rtl), wrap_at_column * 0.6) : 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); + return ts_caret.l_caret.position.x + (rtl ? -wrap_indent : wrap_indent); } else { - return ts_caret.t_caret.position.x + (is_layout_rtl() ? -wrap_indent : wrap_indent); + return ts_caret.t_caret.position.x + (rtl ? -wrap_indent : wrap_indent); } } diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index 17d58c7ce49..e4948decdf0 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -159,6 +159,7 @@ private: int line_count = 0; int height = 0; int width = 0; + float indent_ofs = -1.0; Line() { data_buf.instantiate(); @@ -228,6 +229,7 @@ private: Vector get_line_wrap_ranges(int p_line) const; const Ref get_line_data(int p_line) const; + float get_indent_offset(int p_line, bool p_rtl) const; void set(int p_line, const String &p_text, const Array &p_bidi_override); void set_ime(int p_line, const String &p_text, const Array &p_bidi_override); diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index fc42180d334..41de08423be 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -7344,7 +7344,7 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") { CHECK(text_edit->get_caret_line(1) == 1); CHECK(text_edit->get_caret_column(1) == 1); CHECK(text_edit->get_caret_line(2) == 0); - CHECK(text_edit->get_caret_column(2) == 8); + CHECK(text_edit->get_caret_column(2) == 7); // Add caret above from first line and not first line wrap. text_edit->add_caret_at_carets(false); @@ -7355,9 +7355,9 @@ TEST_CASE("[SceneTree][TextEdit] multicaret") { CHECK(text_edit->get_caret_line(1) == 1); CHECK(text_edit->get_caret_column(1) == 1); CHECK(text_edit->get_caret_line(2) == 0); - CHECK(text_edit->get_caret_column(2) == 8); + CHECK(text_edit->get_caret_column(2) == 7); CHECK(text_edit->get_caret_line(3) == 0); - CHECK(text_edit->get_caret_column(3) == 4); + CHECK(text_edit->get_caret_column(3) == 2); // Cannot add caret above from first line first line wrap. text_edit->remove_secondary_carets();