Introduce a dedicated data structure for text queries

Add an IndexableText structure for text queries.
Instead of linear search for a node this now uses a HashMap.
Remove the now irrelevant fields from TextDisplayItem.
This commit is contained in:
Pyfisch 2018-02-11 21:00:32 +01:00
parent a5115139ba
commit 2d74bcfea5
6 changed files with 70 additions and 49 deletions

View file

@ -332,6 +332,9 @@ pub struct DisplayListBuildState<'a> {
/// Vector containing iframe sizes, used to inform the constellation about
/// new iframe sizes
pub iframe_sizes: Vec<(BrowsingContextId, TypedSize2D<f32, CSSPixel>)>,
/// Stores text runs to answer text queries used to place a cursor inside text.
pub indexable_text: IndexableText,
}
impl<'a> DisplayListBuildState<'a> {
@ -350,6 +353,7 @@ impl<'a> DisplayListBuildState<'a> {
current_stacking_context_id: StackingContextId::root(),
current_clipping_and_scrolling: root_clip_indices,
iframe_sizes: Vec::new(),
indexable_text: IndexableText::default(),
}
}
@ -2175,7 +2179,7 @@ impl FragmentDisplayListBuilding for Fragment {
// Create display items for text decorations.
let text_decorations = self.style().get_inheritedtext().text_decorations_in_effect;
let stacking_relative_content_box = LogicalRect::from_physical(
let logical_stacking_relative_content_box = LogicalRect::from_physical(
self.style.writing_mode,
*stacking_relative_content_box,
container_size,
@ -2183,9 +2187,10 @@ impl FragmentDisplayListBuilding for Fragment {
// Underline
if text_decorations.underline {
let mut stacking_relative_box = stacking_relative_content_box;
stacking_relative_box.start.b =
stacking_relative_content_box.start.b + metrics.ascent - metrics.underline_offset;
let mut stacking_relative_box = logical_stacking_relative_content_box;
stacking_relative_box.start.b = logical_stacking_relative_content_box.start.b +
metrics.ascent -
metrics.underline_offset;
stacking_relative_box.size.block = metrics.underline_size;
self.build_display_list_for_text_decoration(
state,
@ -2197,7 +2202,7 @@ impl FragmentDisplayListBuilding for Fragment {
// Overline
if text_decorations.overline {
let mut stacking_relative_box = stacking_relative_content_box;
let mut stacking_relative_box = logical_stacking_relative_content_box;
stacking_relative_box.size.block = metrics.underline_size;
self.build_display_list_for_text_decoration(
state,
@ -2214,11 +2219,16 @@ impl FragmentDisplayListBuilding for Fragment {
baseline_origin,
);
if !glyphs.is_empty() {
state.add_display_item(DisplayItem::Text(Box::new(TextDisplayItem {
base: base.clone(),
let indexable_text = IndexableTextItem {
origin: stacking_relative_content_box.origin,
text_run: text_fragment.run.clone(),
range: text_fragment.range,
baseline_origin: baseline_origin.to_layout(),
baseline_origin,
};
state.indexable_text.insert(self.node, indexable_text);
state.add_display_item(DisplayItem::Text(Box::new(TextDisplayItem {
base: base.clone(),
glyphs: glyphs,
font_key: text_fragment.run.font_key,
text_color: text_color.to_layout(),
@ -2230,7 +2240,7 @@ impl FragmentDisplayListBuilding for Fragment {
// Line-Through
if text_decorations.line_through {
let mut stacking_relative_box = stacking_relative_content_box;
let mut stacking_relative_box = logical_stacking_relative_content_box;
stacking_relative_box.start.b =
stacking_relative_box.start.b + metrics.ascent - metrics.strikeout_offset;
stacking_relative_box.size.block = metrics.strikeout_size;
@ -3238,3 +3248,42 @@ pub struct BackgroundPlacement {
/// measures above it is only shown within these bounds.
css_clip: Rect<Au>,
}
pub struct IndexableTextItem {
/// The placement of the text item on the plane.
pub origin: Point2D<Au>,
/// The text run.
pub text_run: Arc<TextRun>,
/// The range of text within the text run.
pub range: Range<ByteIndex>,
/// The position of the start of the baseline of this text.
pub baseline_origin: Point2D<Au>,
}
#[derive(Default)]
pub struct IndexableText {
inner: FnvHashMap<OpaqueNode, IndexableTextItem>,
}
impl IndexableText {
fn insert(&mut self, node: OpaqueNode, item: IndexableTextItem) {
// Guard against duplicate nodes.
if self.inner.insert(node, item).is_some() {
debug!(
"TODO(#20020): Text indexing for {:?} is broken because of multiple text runs.",
node
);
};
}
// Returns the text index within a node for the point of interest.
pub fn text_index(&self, node: OpaqueNode, point_in_item: Point2D<Au>) -> Option<usize> {
if let Some(item) = self.inner.get(&node) {
let point = point_in_item + item.origin.to_vector();
let offset = point - item.baseline_origin;
Some(item.text_run.range_index_of_advance(&item.range, offset.x))
} else {
None
}
}
}