Allow layout to be calculated without specifying width
This commit is contained in:
parent
a3a6262e5d
commit
b288de13ae
4 changed files with 66 additions and 32 deletions
|
|
@ -294,7 +294,7 @@ impl Buffer {
|
||||||
&mut self.scratch,
|
&mut self.scratch,
|
||||||
font_system,
|
font_system,
|
||||||
self.metrics.font_size,
|
self.metrics.font_size,
|
||||||
self.width,
|
Some(self.width),
|
||||||
self.wrap,
|
self.wrap,
|
||||||
self.monospace_width,
|
self.monospace_width,
|
||||||
self.tab_width,
|
self.tab_width,
|
||||||
|
|
@ -528,7 +528,7 @@ impl Buffer {
|
||||||
&mut self.scratch,
|
&mut self.scratch,
|
||||||
font_system,
|
font_system,
|
||||||
self.metrics.font_size,
|
self.metrics.font_size,
|
||||||
self.width,
|
Some(self.width),
|
||||||
self.wrap,
|
self.wrap,
|
||||||
self.monospace_width,
|
self.monospace_width,
|
||||||
self.tab_width,
|
self.tab_width,
|
||||||
|
|
|
||||||
|
|
@ -217,7 +217,7 @@ impl BufferLine {
|
||||||
&mut self,
|
&mut self,
|
||||||
font_system: &mut FontSystem,
|
font_system: &mut FontSystem,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
width: f32,
|
width_opt: Option<f32>,
|
||||||
wrap: Wrap,
|
wrap: Wrap,
|
||||||
match_mono_width: Option<f32>,
|
match_mono_width: Option<f32>,
|
||||||
tab_width: u16,
|
tab_width: u16,
|
||||||
|
|
@ -226,7 +226,7 @@ impl BufferLine {
|
||||||
&mut ShapeBuffer::default(),
|
&mut ShapeBuffer::default(),
|
||||||
font_system,
|
font_system,
|
||||||
font_size,
|
font_size,
|
||||||
width,
|
width_opt,
|
||||||
wrap,
|
wrap,
|
||||||
match_mono_width,
|
match_mono_width,
|
||||||
tab_width,
|
tab_width,
|
||||||
|
|
@ -239,7 +239,7 @@ impl BufferLine {
|
||||||
scratch: &mut ShapeBuffer,
|
scratch: &mut ShapeBuffer,
|
||||||
font_system: &mut FontSystem,
|
font_system: &mut FontSystem,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
width: f32,
|
width_opt: Option<f32>,
|
||||||
wrap: Wrap,
|
wrap: Wrap,
|
||||||
match_mono_width: Option<f32>,
|
match_mono_width: Option<f32>,
|
||||||
tab_width: u16,
|
tab_width: u16,
|
||||||
|
|
@ -251,7 +251,7 @@ impl BufferLine {
|
||||||
shape.layout_to_buffer(
|
shape.layout_to_buffer(
|
||||||
scratch,
|
scratch,
|
||||||
font_size,
|
font_size,
|
||||||
width,
|
width_opt,
|
||||||
wrap,
|
wrap,
|
||||||
align,
|
align,
|
||||||
&mut layout,
|
&mut layout,
|
||||||
|
|
|
||||||
37
src/shape.rs
37
src/shape.rs
|
|
@ -992,7 +992,7 @@ impl ShapeLine {
|
||||||
pub fn layout(
|
pub fn layout(
|
||||||
&self,
|
&self,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
line_width: f32,
|
width_opt: Option<f32>,
|
||||||
wrap: Wrap,
|
wrap: Wrap,
|
||||||
align: Option<Align>,
|
align: Option<Align>,
|
||||||
match_mono_width: Option<f32>,
|
match_mono_width: Option<f32>,
|
||||||
|
|
@ -1001,7 +1001,7 @@ impl ShapeLine {
|
||||||
self.layout_to_buffer(
|
self.layout_to_buffer(
|
||||||
&mut ShapeBuffer::default(),
|
&mut ShapeBuffer::default(),
|
||||||
font_size,
|
font_size,
|
||||||
line_width,
|
width_opt,
|
||||||
wrap,
|
wrap,
|
||||||
align,
|
align,
|
||||||
&mut lines,
|
&mut lines,
|
||||||
|
|
@ -1014,7 +1014,7 @@ impl ShapeLine {
|
||||||
&self,
|
&self,
|
||||||
scratch: &mut ShapeBuffer,
|
scratch: &mut ShapeBuffer,
|
||||||
font_size: f32,
|
font_size: f32,
|
||||||
line_width: f32,
|
width_opt: Option<f32>,
|
||||||
wrap: Wrap,
|
wrap: Wrap,
|
||||||
align: Option<Align>,
|
align: Option<Align>,
|
||||||
layout_lines: &mut Vec<LayoutLine>,
|
layout_lines: &mut Vec<LayoutLine>,
|
||||||
|
|
@ -1089,11 +1089,11 @@ impl ShapeLine {
|
||||||
// relayouts with that width as the `line_width` will produce the same
|
// relayouts with that width as the `line_width` will produce the same
|
||||||
// wrapping results.
|
// wrapping results.
|
||||||
if current_visual_line.w + (word_range_width + word_width)
|
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
|
// Include one blank word over the width limit since it won't be
|
||||||
// counted in the final width
|
// counted in the final width
|
||||||
|| (word.blank
|
|| (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
|
// fits
|
||||||
if word.blank {
|
if word.blank {
|
||||||
|
|
@ -1104,12 +1104,12 @@ impl ShapeLine {
|
||||||
continue;
|
continue;
|
||||||
} else if wrap == Wrap::Glyph
|
} 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.
|
// 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.
|
// Commit the current line so that the word starts on the next line.
|
||||||
if word_range_width > 0.
|
if word_range_width > 0.
|
||||||
&& wrap == Wrap::WordOrGlyph
|
&& wrap == Wrap::WordOrGlyph
|
||||||
&& word_width > line_width
|
&& word_width > width_opt.unwrap_or(f32::INFINITY)
|
||||||
{
|
{
|
||||||
add_to_visual_line(
|
add_to_visual_line(
|
||||||
&mut current_visual_line,
|
&mut current_visual_line,
|
||||||
|
|
@ -1132,7 +1132,7 @@ impl ShapeLine {
|
||||||
for (glyph_i, glyph) in word.glyphs.iter().enumerate().rev() {
|
for (glyph_i, glyph) in word.glyphs.iter().enumerate().rev() {
|
||||||
let glyph_width = glyph.width(font_size);
|
let glyph_width = glyph.width(font_size);
|
||||||
if current_visual_line.w + (word_range_width + glyph_width)
|
if current_visual_line.w + (word_range_width + glyph_width)
|
||||||
<= line_width
|
<= width_opt.unwrap_or(f32::INFINITY)
|
||||||
{
|
{
|
||||||
word_range_width += glyph_width;
|
word_range_width += glyph_width;
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1213,11 +1213,11 @@ impl ShapeLine {
|
||||||
for (i, word) in span.words.iter().enumerate() {
|
for (i, word) in span.words.iter().enumerate() {
|
||||||
let word_width = word.width(font_size);
|
let word_width = word.width(font_size);
|
||||||
if current_visual_line.w + (word_range_width + word_width)
|
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
|
// Include one blank word over the width limit since it won't be
|
||||||
// counted in the final width.
|
// counted in the final width.
|
||||||
|| (word.blank
|
|| (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
|
// fits
|
||||||
if word.blank {
|
if word.blank {
|
||||||
|
|
@ -1228,12 +1228,12 @@ impl ShapeLine {
|
||||||
continue;
|
continue;
|
||||||
} else if wrap == Wrap::Glyph
|
} 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.
|
// 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.
|
// Commit the current line so that the word starts on the next line.
|
||||||
if word_range_width > 0.
|
if word_range_width > 0.
|
||||||
&& wrap == Wrap::WordOrGlyph
|
&& wrap == Wrap::WordOrGlyph
|
||||||
&& word_width > line_width
|
&& word_width > width_opt.unwrap_or(f32::INFINITY)
|
||||||
{
|
{
|
||||||
add_to_visual_line(
|
add_to_visual_line(
|
||||||
&mut current_visual_line,
|
&mut current_visual_line,
|
||||||
|
|
@ -1256,7 +1256,7 @@ impl ShapeLine {
|
||||||
for (glyph_i, glyph) in word.glyphs.iter().enumerate() {
|
for (glyph_i, glyph) in word.glyphs.iter().enumerate() {
|
||||||
let glyph_width = glyph.width(font_size);
|
let glyph_width = glyph.width(font_size);
|
||||||
if current_visual_line.w + (word_range_width + glyph_width)
|
if current_visual_line.w + (word_range_width + glyph_width)
|
||||||
<= line_width
|
<= width_opt.unwrap_or(f32::INFINITY)
|
||||||
{
|
{
|
||||||
word_range_width += glyph_width;
|
word_range_width += glyph_width;
|
||||||
continue;
|
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 start_x = if self.rtl { line_width } else { 0.0 };
|
||||||
|
|
||||||
let number_of_visual_lines = visual_lines.len();
|
let number_of_visual_lines = visual_lines.len();
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,17 @@ fn stable_wrap() {
|
||||||
let font = std::fs::read("fonts/FiraMono-Medium.ttf").unwrap();
|
let font = std::fs::read("fonts/FiraMono-Medium.ttf").unwrap();
|
||||||
font_system.db_mut().load_font_data(font);
|
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 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 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);
|
let bounded_max_width = layout_bounded.iter().map(|l| l.w).fold(0.0, f32::max);
|
||||||
|
|
||||||
// For debugging:
|
// For debugging:
|
||||||
|
|
@ -37,10 +40,13 @@ fn stable_wrap() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(max_width, layout_unbounded.len()),
|
(max_width, layout_unbounded.len()),
|
||||||
(bounded_max_width, layout_bounded.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()) {
|
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));
|
.chain(BidiParagraphs::new(&hello_sample));
|
||||||
|
|
||||||
for text in cases {
|
for text in cases {
|
||||||
for wrap in [Wrap::Word, Wrap::Glyph] {
|
for wrap in [Wrap::None, Wrap::Glyph, Wrap::Word, Wrap::WordOrGlyph] {
|
||||||
for start_width in [f32::MAX, 80.0, 198.2132, 20.0, 4.0, 300.0] {
|
for align_opt in [
|
||||||
check_wrap(text, wrap, start_width);
|
None,
|
||||||
let with_spaces = format!("{text} ");
|
Some(Align::Left),
|
||||||
check_wrap(&with_spaces, wrap, start_width);
|
Some(Align::Right),
|
||||||
let with_spaces_2 = format!("{text} ");
|
Some(Align::Center),
|
||||||
check_wrap(&with_spaces_2, wrap, start_width);
|
//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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue