diff --git a/src/buffer.rs b/src/buffer.rs index 0a035a2..09befd3 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -294,7 +294,7 @@ impl Buffer { &mut self.scratch, font_system, self.metrics.font_size, - self.width, + Some(self.width), self.wrap, self.monospace_width, self.tab_width, @@ -528,7 +528,7 @@ impl Buffer { &mut self.scratch, font_system, self.metrics.font_size, - self.width, + Some(self.width), self.wrap, self.monospace_width, self.tab_width, diff --git a/src/buffer_line.rs b/src/buffer_line.rs index f028bf7..9eba6c7 100644 --- a/src/buffer_line.rs +++ b/src/buffer_line.rs @@ -217,7 +217,7 @@ impl BufferLine { &mut self, font_system: &mut FontSystem, font_size: f32, - width: f32, + width_opt: Option, wrap: Wrap, match_mono_width: Option, tab_width: u16, @@ -226,7 +226,7 @@ impl BufferLine { &mut ShapeBuffer::default(), font_system, font_size, - width, + width_opt, wrap, match_mono_width, tab_width, @@ -239,7 +239,7 @@ impl BufferLine { scratch: &mut ShapeBuffer, font_system: &mut FontSystem, font_size: f32, - width: f32, + width_opt: Option, wrap: Wrap, match_mono_width: Option, tab_width: u16, @@ -251,7 +251,7 @@ impl BufferLine { shape.layout_to_buffer( scratch, font_size, - width, + width_opt, wrap, align, &mut layout, diff --git a/src/shape.rs b/src/shape.rs index 119e87d..251931b 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -992,7 +992,7 @@ impl ShapeLine { pub fn layout( &self, font_size: f32, - line_width: f32, + width_opt: Option, wrap: Wrap, align: Option, match_mono_width: Option, @@ -1001,7 +1001,7 @@ impl ShapeLine { self.layout_to_buffer( &mut ShapeBuffer::default(), font_size, - line_width, + width_opt, wrap, align, &mut lines, @@ -1014,7 +1014,7 @@ impl ShapeLine { &self, scratch: &mut ShapeBuffer, font_size: f32, - line_width: f32, + width_opt: Option, wrap: Wrap, align: Option, layout_lines: &mut Vec, @@ -1089,11 +1089,11 @@ impl ShapeLine { // relayouts with that width as the `line_width` will produce the same // wrapping results. if current_visual_line.w + (word_range_width + word_width) - <= line_width + <= width_opt.unwrap_or(f32::INFINITY) // Include one blank word over the width limit since it won't be // counted in the final width || (word.blank - && (current_visual_line.w + word_range_width) <= line_width) + && (current_visual_line.w + word_range_width) <= width_opt.unwrap_or(f32::INFINITY)) { // fits if word.blank { @@ -1104,12 +1104,12 @@ impl ShapeLine { continue; } else if wrap == Wrap::Glyph // Make sure that the word is able to fit on it's own line, if not, fall back to Glyph wrapping. - || (wrap == Wrap::WordOrGlyph && word_width > line_width) + || (wrap == Wrap::WordOrGlyph && word_width > width_opt.unwrap_or(f32::INFINITY)) { // Commit the current line so that the word starts on the next line. if word_range_width > 0. && wrap == Wrap::WordOrGlyph - && word_width > line_width + && word_width > width_opt.unwrap_or(f32::INFINITY) { add_to_visual_line( &mut current_visual_line, @@ -1132,7 +1132,7 @@ impl ShapeLine { for (glyph_i, glyph) in word.glyphs.iter().enumerate().rev() { let glyph_width = glyph.width(font_size); if current_visual_line.w + (word_range_width + glyph_width) - <= line_width + <= width_opt.unwrap_or(f32::INFINITY) { word_range_width += glyph_width; continue; @@ -1213,11 +1213,11 @@ impl ShapeLine { for (i, word) in span.words.iter().enumerate() { let word_width = word.width(font_size); if current_visual_line.w + (word_range_width + word_width) - <= line_width + <= width_opt.unwrap_or(f32::INFINITY) // Include one blank word over the width limit since it won't be // counted in the final width. || (word.blank - && (current_visual_line.w + word_range_width) <= line_width) + && (current_visual_line.w + word_range_width) <= width_opt.unwrap_or(f32::INFINITY)) { // fits if word.blank { @@ -1228,12 +1228,12 @@ impl ShapeLine { continue; } else if wrap == Wrap::Glyph // Make sure that the word is able to fit on it's own line, if not, fall back to Glyph wrapping. - || (wrap == Wrap::WordOrGlyph && word_width > line_width) + || (wrap == Wrap::WordOrGlyph && word_width > width_opt.unwrap_or(f32::INFINITY)) { // Commit the current line so that the word starts on the next line. if word_range_width > 0. && wrap == Wrap::WordOrGlyph - && word_width > line_width + && word_width > width_opt.unwrap_or(f32::INFINITY) { add_to_visual_line( &mut current_visual_line, @@ -1256,7 +1256,7 @@ impl ShapeLine { for (glyph_i, glyph) in word.glyphs.iter().enumerate() { let glyph_width = glyph.width(font_size); if current_visual_line.w + (word_range_width + glyph_width) - <= line_width + <= width_opt.unwrap_or(f32::INFINITY) { word_range_width += glyph_width; continue; @@ -1346,6 +1346,17 @@ impl ShapeLine { } }); + let line_width = match width_opt { + Some(width) => width, + None => { + let mut width: f32 = 0.0; + for visual_line in visual_lines.iter() { + width = width.max(visual_line.w); + } + width + } + }; + let start_x = if self.rtl { line_width } else { 0.0 }; let number_of_visual_lines = visual_lines.len(); diff --git a/tests/wrap_stability.rs b/tests/wrap_stability.rs index 382fc25..be48ada 100644 --- a/tests/wrap_stability.rs +++ b/tests/wrap_stability.rs @@ -20,14 +20,17 @@ fn stable_wrap() { let font = std::fs::read("fonts/FiraMono-Medium.ttf").unwrap(); font_system.db_mut().load_font_data(font); - let mut check_wrap = |text: &_, wrap, start_width| { + let mut check_wrap = |text: &_, wrap, align_opt, start_width_opt| { let line = ShapeLine::new(&mut font_system, text, &attrs, Shaping::Advanced, 8); - let layout_unbounded = line.layout(font_size, start_width, wrap, Some(Align::Left), None); + let layout_unbounded = line.layout(font_size, start_width_opt, wrap, align_opt, None); let max_width = layout_unbounded.iter().map(|l| l.w).fold(0.0, f32::max); - let new_limit = f32::min(start_width, max_width); + let new_limit = match start_width_opt { + Some(start_width) => f32::min(start_width, max_width), + None => max_width, + }; - let layout_bounded = line.layout(font_size, new_limit, wrap, Some(Align::Left), None); + let layout_bounded = line.layout(font_size, Some(new_limit), wrap, align_opt, None); let bounded_max_width = layout_bounded.iter().map(|l| l.w).fold(0.0, f32::max); // For debugging: @@ -37,10 +40,13 @@ fn stable_wrap() { assert_eq!( (max_width, layout_unbounded.len()), (bounded_max_width, layout_bounded.len()), - "Wrap \"{wrap:?}\" with text: \"{text}\"", + "Wrap \"{wrap:?}\" and align \"{align_opt:?}\" with text: \"{text}\"", ); for (u, b) in layout_unbounded[1..].iter().zip(layout_bounded[1..].iter()) { - assert_eq!(u.w, b.w, "Wrap {wrap:?} with text: \"{text}\"",); + assert_eq!( + u.w, b.w, + "Wrap {wrap:?} and align \"{align_opt:?}\" with text: \"{text}\"", + ); } }; @@ -59,13 +65,30 @@ fn stable_wrap() { .chain(BidiParagraphs::new(&hello_sample)); for text in cases { - for wrap in [Wrap::Word, Wrap::Glyph] { - for start_width in [f32::MAX, 80.0, 198.2132, 20.0, 4.0, 300.0] { - check_wrap(text, wrap, start_width); - let with_spaces = format!("{text} "); - check_wrap(&with_spaces, wrap, start_width); - let with_spaces_2 = format!("{text} "); - check_wrap(&with_spaces_2, wrap, start_width); + for wrap in [Wrap::None, Wrap::Glyph, Wrap::Word, Wrap::WordOrGlyph] { + for align_opt in [ + None, + Some(Align::Left), + Some(Align::Right), + Some(Align::Center), + //TODO: Align::Justified + Some(Align::End), + ] { + for start_width_opt in [ + None, + Some(f32::MAX), + Some(80.0), + Some(198.2132), + Some(20.0), + Some(4.0), + Some(300.0), + ] { + check_wrap(text, wrap, align_opt, start_width_opt); + let with_spaces = format!("{text} "); + check_wrap(&with_spaces, wrap, align_opt, start_width_opt); + let with_spaces_2 = format!("{text} "); + check_wrap(&with_spaces_2, wrap, align_opt, start_width_opt); + } } } }