From 1b66fec3ece5123e21ba6680ff11d0e8182edebb Mon Sep 17 00:00:00 2001 From: AganzeFelicite Date: Mon, 19 Oct 2026 12:12:54 +0200 Subject: [PATCH] fix(text): the caret after a line break sits on the next line `caret` put the index right after a line break (a newline, or a line separator) at the end of the line above, so after Enter the caret showed on the previous line until something was typed, and Up/Down counted lines from there. The index before the break stays at the end of its line; the one after it is now on the next line, as after a wrap. Selection highlights already started such lines at their own start (#665); their comment no longer describes the old caret. --- crates/text/src/layout.rs | 13 ++++--------- crates/text/src/tests.rs | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/crates/text/src/layout.rs b/crates/text/src/layout.rs index f9139f6f..014bb1cd 100644 --- a/crates/text/src/layout.rs +++ b/crates/text/src/layout.rs @@ -1171,16 +1171,12 @@ impl TextLayout { pub fn caret(&self, index: usize) -> (f64, usize) { for (li, line) in self.lines.iter().enumerate() { let last_line = li + 1 == self.lines.len(); + // A caret at a line's end belongs to the next line, whether the line wraps or ends with a + // line break (before the break it is still on this line: past its glyphs). let in_line = index >= line.char_start && (index < line.char_end || (last_line && index <= line.char_end)); - // A caret at the end of a soft-wrapped line belongs to the next line. - if !(in_line || index == line.char_end && line.break_after && !last_line) { + if !in_line { continue; } - if index == line.char_end && line.break_after && !last_line { - // Before the line break: end of this line. - let x = self.line_end_x(line); - return (x, li); - } let gl = self.glyphs.get(line.glyph_start..line.glyph_end).unwrap_or(&[]); for g in gl { if g.char_end == g.char_start { @@ -1303,8 +1299,7 @@ impl TextLayout { out.extend(Self::mixed_selection(gl, a, b, line)); continue; } - // From the line's own start: `caret(char_start)` after a hard break is the end of the - // line above. + // From the line's own start. let x0 = if a <= line.char_start { self.line_start_x(line) } else { self.caret(a).0 }; let x1 = if b >= line.char_end { // The last line has no paragraph break to show, so it ends at its glyphs. diff --git a/crates/text/src/tests.rs b/crates/text/src/tests.rs index f5eaab6b..ebf5889c 100644 --- a/crates/text/src/tests.rs +++ b/crates/text/src/tests.rs @@ -481,3 +481,19 @@ fn selection_after_a_line_break_starts_at_the_line_start() { // A break followed by text still doesn't highlight the next line's start. assert!(rect_on(&l, &l.selection_rects(0, 6), 1).is_none()); } + +#[test] +fn the_caret_after_a_line_break_sits_on_the_next_line() { + if !have_fonts() { + return; + } + // Before the break: the end of its line; after it: the start of the next (after Enter, the + // caret shows on the new line, and Up/Down count from there). + let l = layout(&TextData::new("ab\ncd"), None, None); + assert_eq!(l.caret(2).1, 0); + assert!(l.caret(2).0 > 0.0); + assert_eq!(l.caret(3), (0.0, 1)); + // A trailing break: the caret sits on the empty last line. + let l = layout(&TextData::new("ab\n"), None, None); + assert_eq!(l.caret(3), (0.0, 1)); +}