diff --git a/core/src/primitive/text.rs b/core/src/primitive/text.rs index 8281225..941f123 100644 --- a/core/src/primitive/text.rs +++ b/core/src/primitive/text.rs @@ -106,6 +106,13 @@ impl Default for TextAttrs { } } +/// How far below the longest line a width may fall and still be answered by +/// the break in hand. A parent that offers a child the length it reported +/// composes that length back through the box chain, so the two differ in the +/// last bits -- and at exactly the longest line, that decides whether a line +/// fits. Sub-pixel, so no break it admits is one a reader could see. +const BREAK_EPSILON_PX: f32 = 0.05; + /// Keeps text and its corresponding layout from getting out of sync. pub struct TextBuffer { text: String, @@ -189,6 +196,23 @@ impl TextBuffer { diag::bump(Counter::TextShapeHits); return; } + // A greedy break at one width is the same break at every width down + // to the longest line it produced: each line still fits, and none can + // take a word that would not fit in the wider box. So the layout in + // hand already answers, and re-breaking would only be a chance to + // disagree with itself -- which is what happens when a parent offers + // a child the length that child just reported, and the two land + // either side of a float. + if let Some(key) = &self.layout_key + && key.attrs == *attrs + && let (Some(broke_at), Some(want)) = (key.max_width, width) + && want <= broke_at + && want + BREAK_EPSILON_PX >= self.layout.width() + { + #[cfg(feature = "layout-diagnostics")] + diag::bump(Counter::TextShapeHits); + return; + } let same_shaping = self .layout_key .as_ref()