Implement word-break: keep_all

This commit is contained in:
Felipe 2016-09-24 17:41:11 +02:00 committed by Felipe Lacerda
parent ef1b594f48
commit d5669ead29
3 changed files with 86 additions and 11 deletions

View file

@ -135,6 +135,53 @@ impl<'a> Iterator for NaturalWordSliceIterator<'a> {
}
}
pub struct SoftWrapSliceIterator<'a> {
text: &'a str,
glyph_run: Option<&'a GlyphRun>,
glyph_run_iter: Iter<'a, GlyphRun>,
range: Range<ByteIndex>,
}
// This is like NaturalWordSliceIterator, except that soft-wrap opportunities
// are allowed. That is, word boundaries are defined solely by UAX#29,
// regardless of whether the sequence being broken into different slices is
// a sequence of alphanumeric characters. This shouldn't make a difference in
// the case of Latin text, but it does in ideographic characters, as well as
// scripts such as Thai.
impl<'a> Iterator for SoftWrapSliceIterator<'a> {
type Item = TextRunSlice<'a>;
#[inline(always)]
fn next(&mut self) -> Option<TextRunSlice<'a>> {
let glyph_run = match self.glyph_run {
None => return None,
Some(glyph_run) => glyph_run,
};
let text_start = self.range.begin();
let text = &self.text[text_start.to_usize()..glyph_run.range.end().to_usize()];
let slice_text = match LineBreakIterator::new(text).next() {
Some((idx, _)) => &text[0..idx],
None => unreachable!()
};
let slice_len = ByteIndex(slice_text.len() as isize);
self.range.adjust_by(slice_len, -slice_len);
if self.range.is_empty() {
self.glyph_run = None
} else if self.range.intersect(&glyph_run.range).is_empty() {
self.glyph_run = self.glyph_run_iter.next();
}
let index_within_glyph_run = text_start - glyph_run.range.begin();
Some(TextRunSlice {
glyphs: &*glyph_run.glyph_store,
offset: glyph_run.range.begin(),
range: Range::new(index_within_glyph_run, slice_len),
})
}
}
pub struct CharacterSliceIterator<'a> {
text: &'a str,
glyph_run: Option<&'a GlyphRun>,
@ -209,8 +256,10 @@ impl<'a> TextRun {
.take_while(|&(_, c)| char_is_whitespace(c)).last() {
whitespace.start = slice.start + i;
slice.end = whitespace.start;
} else if idx != text.len() {
// If there's no whitespace, try increasing the slice.
continue;
}
if slice.len() > 0 {
glyphs.push(GlyphRun {
glyph_store: font.shape_text(&text[slice.clone()], options),
@ -343,6 +392,24 @@ impl<'a> TextRun {
}
}
/// Returns an iterator that will iterate over all slices of glyphs that represent natural
/// words in the given range, where soft wrap opportunities are taken into account.
pub fn soft_wrap_slices_in_range(&'a self, range: &Range<ByteIndex>)
-> SoftWrapSliceIterator<'a> {
let index = match self.index_of_first_glyph_run_containing(range.begin()) {
None => self.glyphs.len(),
Some(index) => index,
};
let mut glyph_run_iter = self.glyphs[index..].iter();
let first_glyph_run = glyph_run_iter.next();
SoftWrapSliceIterator {
text: &self.text,
glyph_run: first_glyph_run,
glyph_run_iter: glyph_run_iter,
range: *range,
}
}
/// Returns an iterator that will iterate over all slices of glyphs that represent individual
/// characters in the given range.
pub fn character_slices_in_range(&'a self, range: &Range<ByteIndex>)

View file

@ -1638,11 +1638,11 @@ impl Fragment {
match self.style().get_inheritedtext().word_break {
word_break::T::normal => {
// Break at normal word boundaries.
let natural_word_breaking_strategy =
text_fragment_info.run.natural_word_slices_in_range(&text_fragment_info.range);
// Break at normal word boundaries, allowing for soft wrap opportunities.
let soft_wrap_breaking_strategy =
text_fragment_info.run.soft_wrap_slices_in_range(&text_fragment_info.range);
self.calculate_split_position_using_breaking_strategy(
natural_word_breaking_strategy,
soft_wrap_breaking_strategy,
max_inline_size,
flags)
}
@ -1655,6 +1655,15 @@ impl Fragment {
character_breaking_strategy,
max_inline_size,
flags)
},
word_break::T::keep_all => {
// Break at word boundaries, and forbid soft wrap opportunities.
let natural_word_breaking_strategy =
text_fragment_info.run.natural_word_slices_in_range(&text_fragment_info.range);
self.calculate_split_position_using_breaking_strategy(
natural_word_breaking_strategy,
max_inline_size,
flags)
}
}
}

View file

@ -385,8 +385,7 @@ ${helpers.single_keyword("overflow-wrap",
// TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support.
${helpers.single_keyword("word-break",
"normal break-all",
extra_gecko_values="keep-all",
"normal break-all keep-all",
gecko_constant_prefix="NS_STYLE_WORDBREAK",
animatable=False)}