Justified
This commit is contained in:
parent
00ff5b72f3
commit
59e89bdbaa
2 changed files with 77 additions and 21 deletions
|
|
@ -320,7 +320,7 @@ impl<'a> Buffer<'a> {
|
||||||
scroll: 0,
|
scroll: 0,
|
||||||
redraw: false,
|
redraw: false,
|
||||||
wrap: Wrap::Word,
|
wrap: Wrap::Word,
|
||||||
align: Align::Center,
|
align: Align::Justified,
|
||||||
};
|
};
|
||||||
buffer.set_text("", Attrs::new());
|
buffer.set_text("", Attrs::new());
|
||||||
buffer
|
buffer
|
||||||
|
|
|
||||||
96
src/shape.rs
96
src/shape.rs
|
|
@ -645,6 +645,7 @@ impl ShapeLine {
|
||||||
for (span_index, span) in self.spans.iter().enumerate() {
|
for (span_index, span) in self.spans.iter().enumerate() {
|
||||||
let mut word_ranges = Vec::new();
|
let mut word_ranges = Vec::new();
|
||||||
let mut word_range_width = 0.;
|
let mut word_range_width = 0.;
|
||||||
|
let mut number_of_blanks = 0;
|
||||||
|
|
||||||
// Create the word ranges that fits in a visual line
|
// Create the word ranges that fits in a visual line
|
||||||
if self.rtl != span.level.is_rtl() {
|
if self.rtl != span.level.is_rtl() {
|
||||||
|
|
@ -656,6 +657,9 @@ impl ShapeLine {
|
||||||
// fits
|
// fits
|
||||||
fit_x -= word_size;
|
fit_x -= word_size;
|
||||||
word_range_width += word_size;
|
word_range_width += word_size;
|
||||||
|
if word.blank {
|
||||||
|
number_of_blanks += 1;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
} else if wrap == Wrap::Glyph {
|
} else if wrap == Wrap::Glyph {
|
||||||
for (glyph_i, glyph) in word.glyphs.iter().enumerate().rev() {
|
for (glyph_i, glyph) in word.glyphs.iter().enumerate().rev() {
|
||||||
|
|
@ -669,7 +673,9 @@ impl ShapeLine {
|
||||||
(i, glyph_i + 1),
|
(i, glyph_i + 1),
|
||||||
fitting_start,
|
fitting_start,
|
||||||
word_range_width,
|
word_range_width,
|
||||||
|
number_of_blanks,
|
||||||
));
|
));
|
||||||
|
number_of_blanks = 0;
|
||||||
fit_x = line_width as f32 - glyph_size;
|
fit_x = line_width as f32 - glyph_size;
|
||||||
word_range_width = glyph_size;
|
word_range_width = glyph_size;
|
||||||
fitting_start = (i, glyph_i + 1);
|
fitting_start = (i, glyph_i + 1);
|
||||||
|
|
@ -677,8 +683,16 @@ impl ShapeLine {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Wrap::Word
|
// Wrap::Word
|
||||||
word_ranges.push(((i + 1, 0), fitting_start, word_range_width));
|
if word.blank && number_of_blanks > 0 {
|
||||||
|
number_of_blanks -= 1;
|
||||||
|
}
|
||||||
|
word_ranges.push((
|
||||||
|
(i + 1, 0),
|
||||||
|
fitting_start,
|
||||||
|
word_range_width,
|
||||||
|
number_of_blanks,
|
||||||
|
));
|
||||||
|
number_of_blanks = 0;
|
||||||
if word.blank {
|
if word.blank {
|
||||||
fit_x = line_width as f32;
|
fit_x = line_width as f32;
|
||||||
word_range_width = 0.;
|
word_range_width = 0.;
|
||||||
|
|
@ -690,7 +704,7 @@ impl ShapeLine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
word_ranges.push(((0, 0), fitting_start, word_range_width));
|
word_ranges.push(((0, 0), fitting_start, word_range_width, number_of_blanks));
|
||||||
} else {
|
} else {
|
||||||
// congruent direction
|
// congruent direction
|
||||||
let mut fitting_start = (0, 0);
|
let mut fitting_start = (0, 0);
|
||||||
|
|
@ -700,6 +714,9 @@ impl ShapeLine {
|
||||||
// fits
|
// fits
|
||||||
fit_x -= word_size;
|
fit_x -= word_size;
|
||||||
word_range_width += word_size;
|
word_range_width += word_size;
|
||||||
|
if word.blank {
|
||||||
|
number_of_blanks += 1;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
} else if wrap == Wrap::Glyph {
|
} else if wrap == Wrap::Glyph {
|
||||||
for (glyph_i, glyph) in word.glyphs.iter().enumerate() {
|
for (glyph_i, glyph) in word.glyphs.iter().enumerate() {
|
||||||
|
|
@ -713,7 +730,9 @@ impl ShapeLine {
|
||||||
fitting_start,
|
fitting_start,
|
||||||
(i, glyph_i),
|
(i, glyph_i),
|
||||||
word_range_width,
|
word_range_width,
|
||||||
|
number_of_blanks,
|
||||||
));
|
));
|
||||||
|
number_of_blanks = 0;
|
||||||
fit_x = line_width as f32 - glyph_size;
|
fit_x = line_width as f32 - glyph_size;
|
||||||
word_range_width = glyph_size;
|
word_range_width = glyph_size;
|
||||||
fitting_start = (i, glyph_i);
|
fitting_start = (i, glyph_i);
|
||||||
|
|
@ -721,7 +740,16 @@ impl ShapeLine {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Wrap::Word
|
// Wrap::Word
|
||||||
word_ranges.push((fitting_start, (i, 0), word_range_width));
|
if word.blank && number_of_blanks > 0 {
|
||||||
|
number_of_blanks -= 1;
|
||||||
|
}
|
||||||
|
word_ranges.push((
|
||||||
|
fitting_start,
|
||||||
|
(i, 0),
|
||||||
|
word_range_width,
|
||||||
|
number_of_blanks,
|
||||||
|
));
|
||||||
|
number_of_blanks = 0;
|
||||||
|
|
||||||
if word.blank {
|
if word.blank {
|
||||||
fit_x = line_width as f32;
|
fit_x = line_width as f32;
|
||||||
|
|
@ -734,7 +762,12 @@ impl ShapeLine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
word_ranges.push((fitting_start, (span.words.len(), 0), word_range_width));
|
word_ranges.push((
|
||||||
|
fitting_start,
|
||||||
|
(span.words.len(), 0),
|
||||||
|
word_range_width,
|
||||||
|
number_of_blanks,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a visual line
|
// Create a visual line
|
||||||
|
|
@ -742,6 +775,7 @@ impl ShapeLine {
|
||||||
(starting_word, starting_glyph),
|
(starting_word, starting_glyph),
|
||||||
(ending_word, ending_glyph),
|
(ending_word, ending_glyph),
|
||||||
word_range_width,
|
word_range_width,
|
||||||
|
number_of_blanks,
|
||||||
) in word_ranges
|
) in word_ranges
|
||||||
{
|
{
|
||||||
// To simplify the algorithm above, we might push empty ranges but we ignore them here
|
// To simplify the algorithm above, we might push empty ranges but we ignore them here
|
||||||
|
|
@ -762,6 +796,7 @@ impl ShapeLine {
|
||||||
(ending_word, ending_glyph),
|
(ending_word, ending_glyph),
|
||||||
));
|
));
|
||||||
current_visual_line.w += word_range_width;
|
current_visual_line.w += word_range_width;
|
||||||
|
current_visual_line.spaces += number_of_blanks;
|
||||||
if self.rtl {
|
if self.rtl {
|
||||||
x -= word_range_width;
|
x -= word_range_width;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -779,6 +814,7 @@ impl ShapeLine {
|
||||||
(ending_word, ending_glyph),
|
(ending_word, ending_glyph),
|
||||||
));
|
));
|
||||||
current_visual_line.w += word_range_width;
|
current_visual_line.w += word_range_width;
|
||||||
|
current_visual_line.spaces += number_of_blanks;
|
||||||
if self.rtl {
|
if self.rtl {
|
||||||
x -= word_range_width;
|
x -= word_range_width;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -800,7 +836,8 @@ impl ShapeLine {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the LayoutLines using the ranges inside visual lines
|
// Create the LayoutLines using the ranges inside visual lines
|
||||||
for visual_line in vl_range_of_spans {
|
let number_of_visual_lines = vl_range_of_spans.len();
|
||||||
|
for (index, visual_line) in vl_range_of_spans.iter().enumerate() {
|
||||||
let new_order = self.reorder(&visual_line.ranges);
|
let new_order = self.reorder(&visual_line.ranges);
|
||||||
let mut glyphs = Vec::with_capacity(1);
|
let mut glyphs = Vec::with_capacity(1);
|
||||||
x = start_x;
|
x = start_x;
|
||||||
|
|
@ -811,10 +848,19 @@ impl ShapeLine {
|
||||||
(Align::Right, true) => 0.,
|
(Align::Right, true) => 0.,
|
||||||
(Align::Right, false) => line_width as f32 - visual_line.w,
|
(Align::Right, false) => line_width as f32 - visual_line.w,
|
||||||
(Align::Center, _) => (line_width as f32 - visual_line.w) / 2.0,
|
(Align::Center, _) => (line_width as f32 - visual_line.w) / 2.0,
|
||||||
(Align::Justified, _) => unimplemented!("I need to work on the number of spaces"),
|
(Align::Justified, _) => {
|
||||||
|
// Don't justify the last line in a paragraph.
|
||||||
|
if visual_line.spaces > 0 && index != number_of_visual_lines - 1 {
|
||||||
|
(line_width as f32 - visual_line.w) / visual_line.spaces as f32
|
||||||
|
} else {
|
||||||
|
0.
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
if self.rtl {
|
if self.rtl {
|
||||||
x -= alignment_correction;
|
if align != Align::Justified {
|
||||||
|
x -= alignment_correction;
|
||||||
|
}
|
||||||
for range in new_order.iter().rev() {
|
for range in new_order.iter().rev() {
|
||||||
for (
|
for (
|
||||||
span_index,
|
span_index,
|
||||||
|
|
@ -824,6 +870,7 @@ impl ShapeLine {
|
||||||
{
|
{
|
||||||
let span = &self.spans[*span_index];
|
let span = &self.spans[*span_index];
|
||||||
if starting_word == ending_word {
|
if starting_word == ending_word {
|
||||||
|
let word_blank = span.words[*starting_word].blank;
|
||||||
for glyph in span.words[*starting_word].glyphs
|
for glyph in span.words[*starting_word].glyphs
|
||||||
[*starting_glyph..*ending_glyph]
|
[*starting_glyph..*ending_glyph]
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -831,6 +878,9 @@ impl ShapeLine {
|
||||||
let x_advance = font_size as f32 * glyph.x_advance;
|
let x_advance = font_size as f32 * glyph.x_advance;
|
||||||
let y_advance = font_size as f32 * glyph.y_advance;
|
let y_advance = font_size as f32 * glyph.y_advance;
|
||||||
x -= x_advance;
|
x -= x_advance;
|
||||||
|
if word_blank && align == Align::Justified {
|
||||||
|
x -= alignment_correction;
|
||||||
|
}
|
||||||
glyphs.push(glyph.layout(font_size, x, y, span.level));
|
glyphs.push(glyph.layout(font_size, x, y, span.level));
|
||||||
y += y_advance;
|
y += y_advance;
|
||||||
}
|
}
|
||||||
|
|
@ -845,16 +895,15 @@ impl ShapeLine {
|
||||||
(0, word.glyphs.len())
|
(0, word.glyphs.len())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let word_blank = word.blank;
|
||||||
for glyph in &word.glyphs[g1..g2] {
|
for glyph in &word.glyphs[g1..g2] {
|
||||||
let x_advance = font_size as f32 * glyph.x_advance;
|
let x_advance = font_size as f32 * glyph.x_advance;
|
||||||
let y_advance = font_size as f32 * glyph.y_advance;
|
let y_advance = font_size as f32 * glyph.y_advance;
|
||||||
if self.rtl {
|
if word_blank && align == Align::Justified {
|
||||||
x -= x_advance;
|
x -= alignment_correction;
|
||||||
}
|
}
|
||||||
|
x -= x_advance;
|
||||||
glyphs.push(glyph.layout(font_size, x, y, span.level));
|
glyphs.push(glyph.layout(font_size, x, y, span.level));
|
||||||
if !self.rtl {
|
|
||||||
x += x_advance;
|
|
||||||
}
|
|
||||||
y += y_advance;
|
y += y_advance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -862,8 +911,12 @@ impl ShapeLine {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else
|
||||||
x += alignment_correction;
|
/* LTR */
|
||||||
|
{
|
||||||
|
if align != Align::Justified {
|
||||||
|
x += alignment_correction;
|
||||||
|
}
|
||||||
for range in new_order {
|
for range in new_order {
|
||||||
for (
|
for (
|
||||||
span_index,
|
span_index,
|
||||||
|
|
@ -873,6 +926,7 @@ impl ShapeLine {
|
||||||
{
|
{
|
||||||
let span = &self.spans[*span_index];
|
let span = &self.spans[*span_index];
|
||||||
if starting_word == ending_word {
|
if starting_word == ending_word {
|
||||||
|
let word_blank = span.words[*starting_word].blank;
|
||||||
for glyph in span.words[*starting_word].glyphs
|
for glyph in span.words[*starting_word].glyphs
|
||||||
[*starting_glyph..*ending_glyph]
|
[*starting_glyph..*ending_glyph]
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -881,6 +935,9 @@ impl ShapeLine {
|
||||||
let y_advance = font_size as f32 * glyph.y_advance;
|
let y_advance = font_size as f32 * glyph.y_advance;
|
||||||
glyphs.push(glyph.layout(font_size, x, y, span.level));
|
glyphs.push(glyph.layout(font_size, x, y, span.level));
|
||||||
x += x_advance;
|
x += x_advance;
|
||||||
|
if word_blank && align == Align::Justified {
|
||||||
|
x += alignment_correction;
|
||||||
|
}
|
||||||
y += y_advance;
|
y += y_advance;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -894,16 +951,15 @@ impl ShapeLine {
|
||||||
(0, word.glyphs.len())
|
(0, word.glyphs.len())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let word_blank = word.blank;
|
||||||
for glyph in &word.glyphs[g1..g2] {
|
for glyph in &word.glyphs[g1..g2] {
|
||||||
let x_advance = font_size as f32 * glyph.x_advance;
|
let x_advance = font_size as f32 * glyph.x_advance;
|
||||||
let y_advance = font_size as f32 * glyph.y_advance;
|
let y_advance = font_size as f32 * glyph.y_advance;
|
||||||
if self.rtl {
|
|
||||||
x -= x_advance;
|
|
||||||
}
|
|
||||||
glyphs.push(glyph.layout(font_size, x, y, span.level));
|
glyphs.push(glyph.layout(font_size, x, y, span.level));
|
||||||
if !self.rtl {
|
if word_blank && align == Align::Justified {
|
||||||
x += x_advance;
|
x += alignment_correction;
|
||||||
}
|
}
|
||||||
|
x += x_advance;
|
||||||
y += y_advance;
|
y += y_advance;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue