Merge pull request #170 from Imberflur/refactor
Some code organization changes in `ShapeLine::layout`
This commit is contained in:
commit
37e8f005e6
1 changed files with 90 additions and 202 deletions
292
src/shape.rs
292
src/shape.rs
|
|
@ -205,11 +205,7 @@ fn shape_run(
|
|||
}
|
||||
}
|
||||
|
||||
log::trace!(
|
||||
" Run {:?}: '{}'",
|
||||
&scratch.scripts,
|
||||
&line[start_run..end_run],
|
||||
);
|
||||
log::trace!(" Run {:?}: '{}'", &scripts, &line[start_run..end_run],);
|
||||
|
||||
let attrs = attrs_list.get_span(start_run);
|
||||
|
||||
|
|
@ -879,17 +875,6 @@ impl ShapeLine {
|
|||
align: Option<Align>,
|
||||
layout_lines: &mut Vec<LayoutLine>,
|
||||
) {
|
||||
let align = align.unwrap_or({
|
||||
if self.rtl {
|
||||
Align::Right
|
||||
} else {
|
||||
Align::Left
|
||||
}
|
||||
});
|
||||
|
||||
// This is used to create a visual line for empty lines (e.g. lines with only a <CR>)
|
||||
let mut push_line = true;
|
||||
|
||||
// For each visual line a list of (span index, and range of words in that span)
|
||||
// Note that a BiDi visual line could have multiple spans or parts of them
|
||||
// let mut vl_range_of_spans = Vec::with_capacity(1);
|
||||
|
|
@ -916,12 +901,6 @@ impl ShapeLine {
|
|||
vl.spaces += number_of_blanks;
|
||||
}
|
||||
|
||||
let start_x = if self.rtl { line_width } else { 0.0 };
|
||||
let mut x;
|
||||
let mut y;
|
||||
let mut max_ascent: f32 = 0.;
|
||||
let mut max_descent: f32 = 0.;
|
||||
|
||||
// This would keep the maximum number of spans that would fit on a visual line
|
||||
// If one span is too large, this variable will hold the range of words inside that span
|
||||
// that fits on a line.
|
||||
|
|
@ -1159,6 +1138,16 @@ impl ShapeLine {
|
|||
}
|
||||
|
||||
// Create the LayoutLines using the ranges inside visual lines
|
||||
let align = align.unwrap_or({
|
||||
if self.rtl {
|
||||
Align::Right
|
||||
} else {
|
||||
Align::Left
|
||||
}
|
||||
});
|
||||
|
||||
let start_x = if self.rtl { line_width } else { 0.0 };
|
||||
|
||||
let number_of_visual_lines = visual_lines.len();
|
||||
for (index, visual_line) in visual_lines.iter().enumerate() {
|
||||
if visual_line.ranges.is_empty() {
|
||||
|
|
@ -1166,10 +1155,10 @@ impl ShapeLine {
|
|||
}
|
||||
let new_order = self.reorder(&visual_line.ranges);
|
||||
let mut glyphs = Vec::with_capacity(1);
|
||||
x = start_x;
|
||||
y = 0.;
|
||||
max_ascent = 0.;
|
||||
max_descent = 0.;
|
||||
let mut x = start_x;
|
||||
let mut y = 0.;
|
||||
let mut max_ascent: f32 = 0.;
|
||||
let mut max_descent: f32 = 0.;
|
||||
let alignment_correction = match (align, self.rtl) {
|
||||
(Align::Left, true) => line_width - visual_line.w,
|
||||
(Align::Left, false) => 0.,
|
||||
|
|
@ -1177,188 +1166,87 @@ impl ShapeLine {
|
|||
(Align::Right, false) => line_width - visual_line.w,
|
||||
(Align::Center, _) => (line_width - visual_line.w) / 2.0,
|
||||
(Align::End, _) => line_width - visual_line.w,
|
||||
(Align::Justified, _) => {
|
||||
// TODO: Only certain `is_whitespace` chars are typically expanded.
|
||||
//
|
||||
// https://www.unicode.org/reports/tr14/#Introduction
|
||||
// > When expanding or compressing interword space according to common
|
||||
// > typographical practice, only the spaces marked by U+0020 SPACE and U+00A0
|
||||
// > NO-BREAK SPACE are subject to compression, and only spaces marked by U+0020
|
||||
// > SPACE, U+00A0 NO-BREAK SPACE, and occasionally spaces marked by U+2009 THIN
|
||||
// > SPACE are subject to expansion. All other space characters normally have
|
||||
// > fixed width.
|
||||
//
|
||||
// (also some spaces aren't followed by potential linebreaks but they could
|
||||
// still be expanded)
|
||||
(Align::Justified, _) => 0.,
|
||||
};
|
||||
|
||||
// Don't justify the last line in a paragraph.
|
||||
if visual_line.spaces > 0 && index != number_of_visual_lines - 1 {
|
||||
(line_width - visual_line.w) / visual_line.spaces as f32
|
||||
} else {
|
||||
0.
|
||||
if self.rtl {
|
||||
x -= alignment_correction;
|
||||
} else {
|
||||
x += alignment_correction;
|
||||
}
|
||||
|
||||
// TODO: Only certain `is_whitespace` chars are typically expanded but this is what is
|
||||
// currently used to compute `visual_line.spaces`.
|
||||
//
|
||||
// https://www.unicode.org/reports/tr14/#Introduction
|
||||
// > When expanding or compressing interword space according to common
|
||||
// > typographical practice, only the spaces marked by U+0020 SPACE and U+00A0
|
||||
// > NO-BREAK SPACE are subject to compression, and only spaces marked by U+0020
|
||||
// > SPACE, U+00A0 NO-BREAK SPACE, and occasionally spaces marked by U+2009 THIN
|
||||
// > SPACE are subject to expansion. All other space characters normally have
|
||||
// > fixed width.
|
||||
//
|
||||
// (also some spaces aren't followed by potential linebreaks but they could
|
||||
// still be expanded)
|
||||
|
||||
// Amount of extra width added to each blank space within a line.
|
||||
let justification_expansion = if matches!(align, Align::Justified)
|
||||
&& visual_line.spaces > 0
|
||||
// Don't justify the last line in a paragraph.
|
||||
&& index != number_of_visual_lines - 1
|
||||
{
|
||||
(line_width - visual_line.w) / visual_line.spaces as f32
|
||||
} else {
|
||||
0.
|
||||
};
|
||||
|
||||
let mut process_range = |range: Range<usize>| {
|
||||
for &(span_index, (starting_word, starting_glyph), (ending_word, ending_glyph)) in
|
||||
visual_line.ranges[range.clone()].iter()
|
||||
{
|
||||
let span = &self.spans[span_index];
|
||||
// If ending_glyph is not 0 we need to include glyphs from the ending_word
|
||||
for i in starting_word..ending_word + usize::from(ending_glyph != 0) {
|
||||
let word = &span.words[i];
|
||||
let included_glyphs = match (i == starting_word, i == ending_word) {
|
||||
(false, false) => &word.glyphs[..],
|
||||
(true, false) => &word.glyphs[starting_glyph..],
|
||||
(false, true) => &word.glyphs[..ending_glyph],
|
||||
(true, true) => &word.glyphs[starting_glyph..ending_glyph],
|
||||
};
|
||||
for glyph in included_glyphs {
|
||||
let x_advance = font_size * glyph.x_advance
|
||||
+ if word.blank {
|
||||
justification_expansion
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let y_advance = font_size * glyph.y_advance;
|
||||
glyphs.push(glyph.layout(font_size, x, y, x_advance, span.level));
|
||||
if self.rtl {
|
||||
x -= x_advance;
|
||||
} else {
|
||||
x += x_advance;
|
||||
}
|
||||
y += y_advance;
|
||||
max_ascent = max_ascent.max(glyph.ascent);
|
||||
max_descent = max_descent.max(glyph.descent);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if self.rtl {
|
||||
if align != Align::Justified {
|
||||
x -= alignment_correction;
|
||||
}
|
||||
for range in new_order.iter().rev() {
|
||||
for (
|
||||
span_index,
|
||||
(starting_word, starting_glyph),
|
||||
(ending_word, ending_glyph),
|
||||
) in visual_line.ranges[range.clone()].iter()
|
||||
{
|
||||
let span = &self.spans[*span_index];
|
||||
if starting_word == ending_word {
|
||||
let word_blank = span.words[*starting_word].blank;
|
||||
for glyph in span.words[*starting_word].glyphs
|
||||
[*starting_glyph..*ending_glyph]
|
||||
.iter()
|
||||
{
|
||||
let x_advance = font_size * glyph.x_advance;
|
||||
let y_advance = font_size * glyph.y_advance;
|
||||
x -= x_advance;
|
||||
if word_blank && align == Align::Justified {
|
||||
x -= alignment_correction;
|
||||
glyphs.push(glyph.layout(
|
||||
font_size,
|
||||
x,
|
||||
y,
|
||||
x_advance + alignment_correction,
|
||||
span.level,
|
||||
));
|
||||
} else {
|
||||
glyphs
|
||||
.push(glyph.layout(font_size, x, y, x_advance, span.level));
|
||||
}
|
||||
y += y_advance;
|
||||
max_ascent = max_ascent.max(glyph.ascent);
|
||||
max_descent = max_descent.max(glyph.descent);
|
||||
}
|
||||
} else {
|
||||
for i in *starting_word..*ending_word + 1 {
|
||||
if let Some(word) = span.words.get(i) {
|
||||
let (g1, g2) = if i == *starting_word {
|
||||
(*starting_glyph, word.glyphs.len())
|
||||
} else if i == *ending_word {
|
||||
(0, *ending_glyph)
|
||||
} else {
|
||||
(0, word.glyphs.len())
|
||||
};
|
||||
|
||||
let word_blank = word.blank;
|
||||
for glyph in &word.glyphs[g1..g2] {
|
||||
let x_advance = font_size * glyph.x_advance;
|
||||
let y_advance = font_size * glyph.y_advance;
|
||||
x -= x_advance;
|
||||
if word_blank && align == Align::Justified {
|
||||
x -= alignment_correction;
|
||||
glyphs.push(glyph.layout(
|
||||
font_size,
|
||||
x,
|
||||
y,
|
||||
x_advance + alignment_correction,
|
||||
span.level,
|
||||
));
|
||||
} else {
|
||||
glyphs
|
||||
.push(glyph.layout(
|
||||
font_size, x, y, x_advance, span.level,
|
||||
));
|
||||
}
|
||||
y += y_advance;
|
||||
max_ascent = max_ascent.max(glyph.ascent);
|
||||
max_descent = max_descent.max(glyph.descent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if self.rtl {
|
||||
for range in new_order.into_iter().rev() {
|
||||
process_range(range);
|
||||
}
|
||||
} else {
|
||||
/* LTR */
|
||||
if align != Align::Justified {
|
||||
x += alignment_correction;
|
||||
}
|
||||
for range in new_order {
|
||||
for (
|
||||
span_index,
|
||||
(starting_word, starting_glyph),
|
||||
(ending_word, ending_glyph),
|
||||
) in visual_line.ranges[range.clone()].iter()
|
||||
{
|
||||
let span = &self.spans[*span_index];
|
||||
if starting_word == ending_word {
|
||||
let word_blank = span.words[*starting_word].blank;
|
||||
for glyph in span.words[*starting_word].glyphs
|
||||
[*starting_glyph..*ending_glyph]
|
||||
.iter()
|
||||
{
|
||||
let x_advance = font_size * glyph.x_advance;
|
||||
let y_advance = font_size * glyph.y_advance;
|
||||
if word_blank && align == Align::Justified {
|
||||
glyphs.push(glyph.layout(
|
||||
font_size,
|
||||
x,
|
||||
y,
|
||||
x_advance + alignment_correction,
|
||||
span.level,
|
||||
));
|
||||
x += alignment_correction;
|
||||
} else {
|
||||
glyphs
|
||||
.push(glyph.layout(font_size, x, y, x_advance, span.level));
|
||||
}
|
||||
x += x_advance;
|
||||
y += y_advance;
|
||||
max_ascent = max_ascent.max(glyph.ascent);
|
||||
max_descent = max_descent.max(glyph.descent);
|
||||
}
|
||||
} else {
|
||||
for i in *starting_word..*ending_word + 1 {
|
||||
if let Some(word) = span.words.get(i) {
|
||||
let (g1, g2) = if i == *starting_word {
|
||||
(*starting_glyph, word.glyphs.len())
|
||||
} else if i == *ending_word {
|
||||
(0, *ending_glyph)
|
||||
} else {
|
||||
(0, word.glyphs.len())
|
||||
};
|
||||
|
||||
let word_blank = word.blank;
|
||||
for glyph in &word.glyphs[g1..g2] {
|
||||
let x_advance = font_size * glyph.x_advance;
|
||||
let y_advance = font_size * glyph.y_advance;
|
||||
if word_blank && align == Align::Justified {
|
||||
glyphs.push(glyph.layout(
|
||||
font_size,
|
||||
x,
|
||||
y,
|
||||
x_advance + alignment_correction,
|
||||
span.level,
|
||||
));
|
||||
x += alignment_correction;
|
||||
} else {
|
||||
glyphs
|
||||
.push(glyph.layout(
|
||||
font_size, x, y, x_advance, span.level,
|
||||
));
|
||||
}
|
||||
x += x_advance;
|
||||
y += y_advance;
|
||||
max_ascent = max_ascent.max(glyph.ascent);
|
||||
max_descent = max_descent.max(glyph.descent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
process_range(range);
|
||||
}
|
||||
}
|
||||
let mut glyphs_swap = Vec::new();
|
||||
mem::swap(&mut glyphs, &mut glyphs_swap);
|
||||
|
||||
layout_lines.push(LayoutLine {
|
||||
w: if align != Align::Justified {
|
||||
visual_line.w
|
||||
|
|
@ -1371,16 +1259,16 @@ impl ShapeLine {
|
|||
},
|
||||
max_ascent: max_ascent * font_size,
|
||||
max_descent: max_descent * font_size,
|
||||
glyphs: glyphs_swap,
|
||||
glyphs,
|
||||
});
|
||||
push_line = false;
|
||||
}
|
||||
|
||||
if push_line {
|
||||
// This is used to create a visual line for empty lines (e.g. lines with only a <CR>)
|
||||
if layout_lines.is_empty() {
|
||||
layout_lines.push(LayoutLine {
|
||||
w: 0.0,
|
||||
max_ascent: max_ascent * font_size,
|
||||
max_descent: max_descent * font_size,
|
||||
max_ascent: 0.0,
|
||||
max_descent: 0.0,
|
||||
glyphs: Default::default(),
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue