mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Position insertion point in input field with mouse
This commit is contained in:
parent
c784bc6944
commit
b40db5b55d
9 changed files with 219 additions and 50 deletions
|
@ -48,10 +48,74 @@ pub struct DisplayList {
|
|||
}
|
||||
|
||||
impl DisplayList {
|
||||
// Returns the text index within a node for the point of interest.
|
||||
pub fn text_index(&self,
|
||||
node: OpaqueNode,
|
||||
client_point: &Point2D<Au>,
|
||||
scroll_offsets: &ScrollOffsetMap)
|
||||
-> Option<usize> {
|
||||
let mut result = Vec::new();
|
||||
let mut translated_point = client_point.clone();
|
||||
let mut traversal = DisplayListTraversal::new(self);
|
||||
self.text_index_contents(node,
|
||||
&mut traversal,
|
||||
&mut translated_point,
|
||||
client_point,
|
||||
scroll_offsets,
|
||||
&mut result);
|
||||
result.pop()
|
||||
}
|
||||
|
||||
pub fn text_index_contents<'a>(&self,
|
||||
node: OpaqueNode,
|
||||
traversal: &mut DisplayListTraversal<'a>,
|
||||
translated_point: &mut Point2D<Au>,
|
||||
client_point: &Point2D<Au>,
|
||||
scroll_offsets: &ScrollOffsetMap,
|
||||
result: &mut Vec<usize>) {
|
||||
while let Some(item) = traversal.next() {
|
||||
match item {
|
||||
&DisplayItem::PushStackingContext(ref stacking_context_item) => {
|
||||
DisplayList::translate_point(&stacking_context_item.stacking_context,
|
||||
translated_point,
|
||||
client_point);
|
||||
self.text_index_contents(node,
|
||||
traversal,
|
||||
translated_point,
|
||||
client_point,
|
||||
scroll_offsets,
|
||||
result);
|
||||
}
|
||||
&DisplayItem::PushScrollRoot(ref item) => {
|
||||
DisplayList::scroll_root(&item.scroll_root,
|
||||
translated_point,
|
||||
scroll_offsets);
|
||||
self.text_index_contents(node,
|
||||
traversal,
|
||||
translated_point,
|
||||
client_point,
|
||||
scroll_offsets,
|
||||
result);
|
||||
|
||||
},
|
||||
&DisplayItem::PopStackingContext(_) => return,
|
||||
&DisplayItem::Text(ref text) => {
|
||||
let base = item.base();
|
||||
if base.metadata.node == node {
|
||||
let offset = *translated_point - text.baseline_origin;
|
||||
let index = text.text_run.range_index_of_advance(&text.range, offset.x);
|
||||
result.push(index);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return all nodes containing the point of interest, bottommost first, and
|
||||
// respecting the `pointer-events` CSS property.
|
||||
pub fn hit_test(&self,
|
||||
translated_point: &Point2D<Au>,
|
||||
translated_point: &mut Point2D<Au>,
|
||||
client_point: &Point2D<Au>,
|
||||
scroll_offsets: &ScrollOffsetMap)
|
||||
-> Vec<DisplayItemMetadata> {
|
||||
|
@ -67,27 +131,31 @@ impl DisplayList {
|
|||
|
||||
pub fn hit_test_contents<'a>(&self,
|
||||
traversal: &mut DisplayListTraversal<'a>,
|
||||
translated_point: &Point2D<Au>,
|
||||
translated_point: &mut Point2D<Au>,
|
||||
client_point: &Point2D<Au>,
|
||||
scroll_offsets: &ScrollOffsetMap,
|
||||
result: &mut Vec<DisplayItemMetadata>) {
|
||||
while let Some(item) = traversal.next() {
|
||||
match item {
|
||||
&DisplayItem::PushStackingContext(ref stacking_context_item) => {
|
||||
self.hit_test_stacking_context(traversal,
|
||||
&stacking_context_item.stacking_context,
|
||||
translated_point,
|
||||
client_point,
|
||||
scroll_offsets,
|
||||
result);
|
||||
DisplayList::translate_point(&stacking_context_item.stacking_context,
|
||||
translated_point,
|
||||
client_point);
|
||||
self.hit_test_contents(traversal,
|
||||
translated_point,
|
||||
client_point,
|
||||
scroll_offsets,
|
||||
result);
|
||||
}
|
||||
&DisplayItem::PushScrollRoot(ref item) => {
|
||||
self.hit_test_scroll_root(traversal,
|
||||
&item.scroll_root,
|
||||
*translated_point,
|
||||
client_point,
|
||||
scroll_offsets,
|
||||
result);
|
||||
DisplayList::scroll_root(&item.scroll_root,
|
||||
translated_point,
|
||||
scroll_offsets);
|
||||
self.hit_test_contents(traversal,
|
||||
translated_point,
|
||||
client_point,
|
||||
scroll_offsets,
|
||||
result);
|
||||
}
|
||||
&DisplayItem::PopStackingContext(_) | &DisplayItem::PopScrollRoot(_) => return,
|
||||
_ => {
|
||||
|
@ -99,38 +167,15 @@ impl DisplayList {
|
|||
}
|
||||
}
|
||||
|
||||
fn hit_test_scroll_root<'a>(&self,
|
||||
traversal: &mut DisplayListTraversal<'a>,
|
||||
scroll_root: &ScrollRoot,
|
||||
mut translated_point: Point2D<Au>,
|
||||
client_point: &Point2D<Au>,
|
||||
scroll_offsets: &ScrollOffsetMap,
|
||||
result: &mut Vec<DisplayItemMetadata>) {
|
||||
// Adjust the translated point to account for the scroll offset if
|
||||
// necessary. This can only happen when WebRender is in use.
|
||||
//
|
||||
// We don't perform this adjustment on the root stacking context because
|
||||
// the DOM-side code has already translated the point for us (e.g. in
|
||||
// `Window::hit_test_query()`) by now.
|
||||
if let Some(scroll_offset) = scroll_offsets.get(&scroll_root.id) {
|
||||
translated_point.x -= Au::from_f32_px(scroll_offset.x);
|
||||
translated_point.y -= Au::from_f32_px(scroll_offset.y);
|
||||
}
|
||||
self.hit_test_contents(traversal, &translated_point, client_point, scroll_offsets, result);
|
||||
}
|
||||
|
||||
fn hit_test_stacking_context<'a>(&self,
|
||||
traversal: &mut DisplayListTraversal<'a>,
|
||||
stacking_context: &StackingContext,
|
||||
translated_point: &Point2D<Au>,
|
||||
client_point: &Point2D<Au>,
|
||||
scroll_offsets: &ScrollOffsetMap,
|
||||
result: &mut Vec<DisplayItemMetadata>) {
|
||||
#[inline]
|
||||
fn translate_point<'a>(stacking_context: &StackingContext,
|
||||
translated_point: &mut Point2D<Au>,
|
||||
client_point: &Point2D<Au>) {
|
||||
// Convert the parent translated point into stacking context local transform space if the
|
||||
// stacking context isn't fixed. If it's fixed, we need to use the client point anyway.
|
||||
debug_assert!(stacking_context.context_type == StackingContextType::Real);
|
||||
let is_fixed = stacking_context.scroll_policy == ScrollPolicy::Fixed;
|
||||
let translated_point = if is_fixed {
|
||||
*translated_point = if is_fixed {
|
||||
*client_point
|
||||
} else {
|
||||
let point = *translated_point - stacking_context.bounds.origin;
|
||||
|
@ -139,8 +184,21 @@ impl DisplayList {
|
|||
point.y.to_f32_px()));
|
||||
Point2D::new(Au::from_f32_px(frac_point.x), Au::from_f32_px(frac_point.y))
|
||||
};
|
||||
}
|
||||
|
||||
self.hit_test_contents(traversal, &translated_point, client_point, scroll_offsets, result);
|
||||
#[inline]
|
||||
fn scroll_root<'a>(scroll_root: &ScrollRoot,
|
||||
translated_point: &mut Point2D<Au>,
|
||||
scroll_offsets: &ScrollOffsetMap) {
|
||||
// Adjust the translated point to account for the scroll offset if necessary.
|
||||
//
|
||||
// We don't perform this adjustment on the root stacking context because
|
||||
// the DOM-side code has already translated the point for us (e.g. in
|
||||
// `Window::hit_test_query()`) by now.
|
||||
if let Some(scroll_offset) = scroll_offsets.get(&scroll_root.id) {
|
||||
translated_point.x -= Au::from_f32_px(scroll_offset.x);
|
||||
translated_point.y -= Au::from_f32_px(scroll_offset.y);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print(&self) {
|
||||
|
|
|
@ -546,6 +546,27 @@ impl<'a> GlyphStore {
|
|||
}
|
||||
}
|
||||
|
||||
// Scan the glyphs for a given range until we reach a given advance. Returns the index
|
||||
// and advance of the glyph in the range at the given advance, if reached. Otherwise, returns the
|
||||
// the number of glyphs and the advance for the given range.
|
||||
#[inline]
|
||||
pub fn range_index_of_advance(&self, range: &Range<ByteIndex>, advance: Au, extra_word_spacing: Au) -> (usize, Au) {
|
||||
let mut index = 0;
|
||||
let mut current_advance = Au(0);
|
||||
for glyph in self.iter_glyphs_for_byte_range(range) {
|
||||
if glyph.char_is_space() {
|
||||
current_advance += glyph.advance() + extra_word_spacing
|
||||
} else {
|
||||
current_advance += glyph.advance()
|
||||
}
|
||||
if current_advance > advance {
|
||||
break;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
(index, current_advance)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn advance_for_byte_range(&self, range: &Range<ByteIndex>, extra_word_spacing: Au) -> Au {
|
||||
if range.begin() == ByteIndex(0) && range.end() == self.len() {
|
||||
|
|
|
@ -304,6 +304,21 @@ impl<'a> TextRun {
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns the index in the range of the first glyph advancing over given advance
|
||||
pub fn range_index_of_advance(&self, range: &Range<ByteIndex>, advance: Au) -> usize {
|
||||
// TODO(Issue #199): alter advance direction for RTL
|
||||
// TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text
|
||||
let mut remaining = advance;
|
||||
self.natural_word_slices_in_range(range)
|
||||
.map(|slice| {
|
||||
let (slice_index, slice_advance) =
|
||||
slice.glyphs.range_index_of_advance(&slice.range, remaining, self.extra_word_spacing);
|
||||
remaining -= slice_advance;
|
||||
slice_index
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
/// Returns an iterator that will iterate over all slices of glyphs that represent natural
|
||||
/// words in the given range.
|
||||
pub fn natural_word_slices_in_range(&'a self, range: &Range<ByteIndex>)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue