mirror of
https://github.com/servo/servo.git
synced 2025-07-02 21:13:39 +01:00
Fix insertion point layout for text runs with multiple fragments
Before this fix, if a TextRun containing an insertion point was split into multiple ScannedText fragments, text layout would draw an insertion point inside of each of the fragments. This patch records the insertion point position at most once per TextRun, and copies it only into the appropriate ScannedText fragment.
This commit is contained in:
parent
bcaa62e1bd
commit
831243af7c
1 changed files with 50 additions and 14 deletions
|
@ -170,6 +170,8 @@ impl TextRunScanner {
|
|||
|
||||
// First, transform/compress text of all the nodes.
|
||||
let (mut run_info_list, mut run_info) = (Vec::new(), RunInfo::new());
|
||||
let mut insertion_point = None;
|
||||
|
||||
for (fragment_index, in_fragment) in self.clump.iter().enumerate() {
|
||||
let mut mapping = RunMapping::new(&run_info_list[..], &run_info, fragment_index);
|
||||
let text;
|
||||
|
@ -181,8 +183,12 @@ impl TextRunScanner {
|
|||
}
|
||||
_ => panic!("Expected an unscanned text fragment!"),
|
||||
};
|
||||
let insertion_point = match selection {
|
||||
Some(range) if range.is_empty() => Some(range.begin()),
|
||||
insertion_point = match selection {
|
||||
Some(range) if range.is_empty() => {
|
||||
// `range` is the range within the current fragment. To get the range
|
||||
// within the text run, offset it by the length of the preceding fragments.
|
||||
Some(range.begin() + CharIndex(run_info.character_length as isize))
|
||||
}
|
||||
_ => None
|
||||
};
|
||||
|
||||
|
@ -233,7 +239,6 @@ impl TextRunScanner {
|
|||
mapping.flush(&mut mappings,
|
||||
&mut run_info,
|
||||
&**text,
|
||||
insertion_point,
|
||||
compression,
|
||||
text_transform,
|
||||
&mut last_whitespace,
|
||||
|
@ -241,7 +246,7 @@ impl TextRunScanner {
|
|||
end_position);
|
||||
if run_info.text.len() > 0 {
|
||||
if flush_run {
|
||||
run_info_list.push(run_info);
|
||||
run_info.flush(&mut run_info_list, &mut insertion_point);
|
||||
run_info = RunInfo::new();
|
||||
}
|
||||
mapping = RunMapping::new(&run_info_list[..],
|
||||
|
@ -263,7 +268,6 @@ impl TextRunScanner {
|
|||
mapping.flush(&mut mappings,
|
||||
&mut run_info,
|
||||
&**text,
|
||||
insertion_point,
|
||||
compression,
|
||||
text_transform,
|
||||
&mut last_whitespace,
|
||||
|
@ -272,7 +276,7 @@ impl TextRunScanner {
|
|||
}
|
||||
|
||||
// Push the final run info.
|
||||
run_info_list.push(run_info);
|
||||
run_info.flush(&mut run_info_list, &mut insertion_point);
|
||||
|
||||
// Per CSS 2.1 § 16.4, "when the resultant space between two characters is not the same
|
||||
// as the default space, user agents should not use ligatures." This ensures that, for
|
||||
|
@ -346,11 +350,18 @@ impl TextRunScanner {
|
|||
if requires_line_break_afterward_if_wrapping_on_newlines {
|
||||
flags.insert(REQUIRES_LINE_BREAK_AFTERWARD_IF_WRAPPING_ON_NEWLINES);
|
||||
}
|
||||
|
||||
let insertion_point = if mapping.contains_insertion_point(scanned_run.insertion_point) {
|
||||
scanned_run.insertion_point
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut new_text_fragment_info = box ScannedTextFragmentInfo::new(
|
||||
scanned_run.run,
|
||||
mapping.char_range,
|
||||
text_size,
|
||||
scanned_run.insertion_point,
|
||||
insertion_point,
|
||||
flags);
|
||||
|
||||
let new_metrics = new_text_fragment_info.run.metrics_for_range(&mapping.char_range);
|
||||
|
@ -504,6 +515,26 @@ impl RunInfo {
|
|||
script: Script::Common,
|
||||
}
|
||||
}
|
||||
|
||||
/// Finish processing this RunInfo and add it to the "done" list.
|
||||
///
|
||||
/// * `insertion_point`: The position of the insertion point, in characters relative to the start
|
||||
/// of this text run.
|
||||
fn flush(mut self,
|
||||
list: &mut Vec<RunInfo>,
|
||||
insertion_point: &mut Option<CharIndex>) {
|
||||
if let Some(idx) = *insertion_point {
|
||||
let char_len = CharIndex(self.character_length as isize);
|
||||
if idx <= char_len {
|
||||
// The insertion point is in this text run.
|
||||
self.insertion_point = insertion_point.take()
|
||||
} else {
|
||||
// Continue looking for the insertion point in the next text run.
|
||||
*insertion_point = Some(idx - char_len)
|
||||
}
|
||||
}
|
||||
list.push(self);
|
||||
}
|
||||
}
|
||||
|
||||
/// A mapping from a portion of an unscanned text fragment to the text run we're going to create
|
||||
|
@ -544,7 +575,6 @@ impl RunMapping {
|
|||
mappings: &mut Vec<RunMapping>,
|
||||
run_info: &mut RunInfo,
|
||||
text: &str,
|
||||
insertion_point: Option<CharIndex>,
|
||||
compression: CompressionMode,
|
||||
text_transform: text_transform::T,
|
||||
last_whitespace: &mut bool,
|
||||
|
@ -568,12 +598,6 @@ impl RunMapping {
|
|||
*last_whitespace,
|
||||
is_first_run);
|
||||
|
||||
// Record the position of the insertion point if necessary.
|
||||
if let Some(insertion_point) = insertion_point {
|
||||
run_info.insertion_point =
|
||||
Some(CharIndex(run_info.character_length as isize + insertion_point.0))
|
||||
}
|
||||
|
||||
run_info.character_length = run_info.character_length + character_count;
|
||||
*start_position = end_position;
|
||||
|
||||
|
@ -587,6 +611,18 @@ impl RunMapping {
|
|||
self.char_range.extend_by(CharIndex(character_count as isize));
|
||||
mappings.push(self)
|
||||
}
|
||||
|
||||
/// Is the insertion point for this text run within this mapping?
|
||||
///
|
||||
/// NOTE: We treat the range as inclusive at both ends, since the insertion point can lie
|
||||
/// before the first character *or* after the last character, and should be drawn even if the
|
||||
/// text is empty.
|
||||
fn contains_insertion_point(&self, insertion_point: Option<CharIndex>) -> bool {
|
||||
match insertion_point {
|
||||
None => false,
|
||||
Some(idx) => self.char_range.begin() <= idx && idx <= self.char_range.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue