diff --git a/crates/layout/src/para/lines.rs b/crates/layout/src/para/lines.rs index 585af5a86e..b19ec3baa1 100644 --- a/crates/layout/src/para/lines.rs +++ b/crates/layout/src/para/lines.rs @@ -584,6 +584,13 @@ impl<'e> Breaker<'e> { (LineSpacing::AtLeast(v), None) => natural.max(v), (LineSpacing::Exactly(v), _) => v, }; + // The empty line's caret follows the alignment, as an empty paragraph's does. + let slack = (self.base_right - rp.indent_left).max(0.0); + let shift = match rp.align { + Align::Center => slack / 2.0, + Align::Right => slack, + _ => 0.0, + }; let (width, x0) = (self.width, rp.indent_left); let (x0, left, right) = if self.rtl_para { (width - x0, width - self.base_right, width - x0) } else { (x0, x0, self.base_right) }; self.lines.push(Line { @@ -594,7 +601,7 @@ impl<'e> Breaker<'e> { text_desc: desc, c0: self.n, c1: self.n, - xs: vec![x0], + xs: vec![if self.rtl_para { x0 - shift } else { x0 + shift }], leaders: Vec::new(), end: LineEnd::Para, start: pl.text_len, diff --git a/crates/layout/src/tests.rs b/crates/layout/src/tests.rs index 229d90b004..22cfedce2e 100644 --- a/crates/layout/src/tests.rs +++ b/crates/layout/src/tests.rs @@ -1422,6 +1422,32 @@ fn center_and_right_alignment() { assert!((x2 - 540.0).abs() < 1.0, "{x2}"); } +#[test] +fn caret_on_an_empty_line_after_a_break_follows_alignment() { + // "BAB I", Shift+Enter: the empty line's caret sits where its alignment puts it (#4283). + let text = format!("BAB I{}", wordcraft_doc::para::LINE_BREAK); + for rtl in [false, true] { + for (align, ltr_x) in [(Align::Left, 72.0), (Align::Center, 306.0), (Align::Right, 540.0), (Align::Justify, 72.0)] { + let mut d = Document::new(); + d.body = vec![wordcraft_doc::para_block(wordcraft_doc::Paragraph::with_text(&text, Default::default()))]; + let at = Pos::body(0, 0); + d.format_paragraphs(&at, &at, &|p: &mut ParaProps| { + p.align = Some(align); + p.bidi = Some(rtl); + }) + .unwrap(); + let expected = if rtl { 612.0 - ltr_x } else { ltr_x }; + let x = lay(&d).caret(&Pos::body(0, text.len())).unwrap().x; + assert!((x - expected).abs() < 1.0, "{align:?} rtl={rtl}: {x}, expected {expected}"); + } + } + // An empty centred paragraph without a break was already centred. + let mut d = Document::from_text(""); + d.format_paragraphs(&Pos::body(0, 0), &Pos::body(0, 0), &|p| p.align = Some(Align::Center)).unwrap(); + let x = lay(&d).caret(&Pos::body(0, 0)).unwrap().x; + assert!((x - 306.0).abs() < 1.0, "{x}"); +} + #[test] fn tabs_land_on_default_stops() { let d = Document::from_text("a\tb");