mirror of
https://github.com/servo/servo.git
synced 2025-08-06 22:15:33 +01:00
Implement word-break: keep_all
This commit is contained in:
parent
ef1b594f48
commit
d5669ead29
3 changed files with 86 additions and 11 deletions
|
@ -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> {
|
pub struct CharacterSliceIterator<'a> {
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
glyph_run: Option<&'a GlyphRun>,
|
glyph_run: Option<&'a GlyphRun>,
|
||||||
|
@ -206,11 +253,13 @@ impl<'a> TextRun {
|
||||||
// Split off any trailing whitespace into a separate glyph run.
|
// Split off any trailing whitespace into a separate glyph run.
|
||||||
let mut whitespace = slice.end..slice.end;
|
let mut whitespace = slice.end..slice.end;
|
||||||
if let Some((i, _)) = word.char_indices().rev()
|
if let Some((i, _)) = word.char_indices().rev()
|
||||||
.take_while(|&(_, c)| char_is_whitespace(c)).last() {
|
.take_while(|&(_, c)| char_is_whitespace(c)).last() {
|
||||||
whitespace.start = slice.start + i;
|
whitespace.start = slice.start + i;
|
||||||
slice.end = whitespace.start;
|
slice.end = whitespace.start;
|
||||||
}
|
} else if idx != text.len() {
|
||||||
|
// If there's no whitespace, try increasing the slice.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if slice.len() > 0 {
|
if slice.len() > 0 {
|
||||||
glyphs.push(GlyphRun {
|
glyphs.push(GlyphRun {
|
||||||
glyph_store: font.shape_text(&text[slice.clone()], options),
|
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
|
/// Returns an iterator that will iterate over all slices of glyphs that represent individual
|
||||||
/// characters in the given range.
|
/// characters in the given range.
|
||||||
pub fn character_slices_in_range(&'a self, range: &Range<ByteIndex>)
|
pub fn character_slices_in_range(&'a self, range: &Range<ByteIndex>)
|
||||||
|
|
|
@ -1638,11 +1638,11 @@ impl Fragment {
|
||||||
|
|
||||||
match self.style().get_inheritedtext().word_break {
|
match self.style().get_inheritedtext().word_break {
|
||||||
word_break::T::normal => {
|
word_break::T::normal => {
|
||||||
// Break at normal word boundaries.
|
// Break at normal word boundaries, allowing for soft wrap opportunities.
|
||||||
let natural_word_breaking_strategy =
|
let soft_wrap_breaking_strategy =
|
||||||
text_fragment_info.run.natural_word_slices_in_range(&text_fragment_info.range);
|
text_fragment_info.run.soft_wrap_slices_in_range(&text_fragment_info.range);
|
||||||
self.calculate_split_position_using_breaking_strategy(
|
self.calculate_split_position_using_breaking_strategy(
|
||||||
natural_word_breaking_strategy,
|
soft_wrap_breaking_strategy,
|
||||||
max_inline_size,
|
max_inline_size,
|
||||||
flags)
|
flags)
|
||||||
}
|
}
|
||||||
|
@ -1655,6 +1655,15 @@ impl Fragment {
|
||||||
character_breaking_strategy,
|
character_breaking_strategy,
|
||||||
max_inline_size,
|
max_inline_size,
|
||||||
flags)
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -385,8 +385,7 @@ ${helpers.single_keyword("overflow-wrap",
|
||||||
|
|
||||||
// TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support.
|
// TODO(pcwalton): Support `word-break: keep-all` once we have better CJK support.
|
||||||
${helpers.single_keyword("word-break",
|
${helpers.single_keyword("word-break",
|
||||||
"normal break-all",
|
"normal break-all keep-all",
|
||||||
extra_gecko_values="keep-all",
|
|
||||||
gecko_constant_prefix="NS_STYLE_WORDBREAK",
|
gecko_constant_prefix="NS_STYLE_WORDBREAK",
|
||||||
animatable=False)}
|
animatable=False)}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue