Skip to content

Commit

Permalink
web/core: Implement basic hit testing
Browse files Browse the repository at this point in the history
We cannot track where within a node (text node for example) we clicked, but we can
find *the* node that was clicked.
  • Loading branch information
simonwuelker committed Sep 17, 2023
1 parent 8ff83b8 commit 4cdea78
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 11 deletions.
55 changes: 48 additions & 7 deletions web/core/src/css/fragment_tree/fragment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ use std::rc::Rc;

use math::Rectangle;

use crate::css::{
display_list::Painter,
layout::{CSSPixels, Sides},
properties::BackgroundColorValue,
stylecomputer::ComputedStyle,
values::color::Color,
FontMetrics,
use crate::{
css::{
display_list::Painter,
layout::{CSSPixels, Sides},
properties::BackgroundColorValue,
stylecomputer::ComputedStyle,
values::color::Color,
FontMetrics,
},
dom::{self, dom_objects, DOMPtr},
};

#[derive(Clone, Debug)]
pub struct BoxFragment {
dom_node: Option<DOMPtr<dom_objects::Node>>,
style: Rc<ComputedStyle>,
margin: Sides<CSSPixels>,
content_area: Rectangle<CSSPixels>,
Expand Down Expand Up @@ -48,6 +52,35 @@ impl Fragment {
Self::Text(text_fragment) => text_fragment.area,
}
}

pub fn hit_test(&self, point: math::Vec2D<CSSPixels>) -> Option<dom::BoundaryPoint> {
match self {
Self::Box(box_fragment) => {
log::info!(
"hit testing a {:?}",
box_fragment.dom_node.as_ref().map(DOMPtr::underlying_type)
);

let first_hit_child = box_fragment.children().iter().find(|child| {
child
.content_area_including_overflow()
.contains_point(point)
});

if let Some(hit_child) = first_hit_child {
hit_child.hit_test(point)
} else {
// None of our children were hit (or we have no children)
// In this case, this fragment is the target of the hit-test
Some(dom::BoundaryPoint::new(box_fragment.dom_node.clone()?, 0))
}
},
Self::Text(text_fragment) => {
log::info!("Clicked a text fragment! {:?}", text_fragment.text());
None
},
}
}
}

impl TextFragment {
Expand Down Expand Up @@ -85,13 +118,15 @@ impl TextFragment {
impl BoxFragment {
#[must_use]
pub fn new(
dom_node: Option<DOMPtr<dom_objects::Node>>,
style: Rc<ComputedStyle>,
margin: Sides<CSSPixels>,
content_area: Rectangle<CSSPixels>,
content_area_including_overflow: Rectangle<CSSPixels>,
children: Vec<Fragment>,
) -> Self {
Self {
dom_node,
style,
margin,
content_area,
Expand Down Expand Up @@ -123,6 +158,12 @@ impl BoxFragment {
self.margin.surround(self.content_area)
}

#[inline]
#[must_use]
pub fn content_area_including_overflow(&self) -> Rectangle<CSSPixels> {
self.content_area_including_overflow
}

pub fn fill_display_list(&self, painter: &mut Painter) {
match self.style.background_color() {
BackgroundColorValue::Transparent => {
Expand Down
20 changes: 18 additions & 2 deletions web/core/src/css/fragment_tree/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ mod fragment;

pub use fragment::{BoxFragment, Fragment, TextFragment};

use super::display_list::Painter;
use crate::dom;

#[derive(Clone, Debug)]
use super::{display_list::Painter, layout::CSSPixels};

#[derive(Clone, Debug, Default)]
pub struct FragmentTree {
root_fragments: Vec<Fragment>,
}
Expand All @@ -24,4 +26,18 @@ impl FragmentTree {
fragment.fill_display_list(painter);
}
}

pub fn hit_test(&self, point: math::Vec2D<CSSPixels>) -> Option<dom::BoundaryPoint> {
for fragment in &self.root_fragments {
if fragment
.content_area_including_overflow()
.contains_point(point)
{
if let Some(hit) = fragment.hit_test(point) {
return Some(hit);
}
}
}
None
}
}
6 changes: 4 additions & 2 deletions web/core/src/css/layout/flow/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,8 @@ impl BlockLevelBox {
let mut cursor = top_left;
for block_box in block_level_boxes {
let box_fragment = block_box.fragment(cursor, containing_block);
content_area_including_overflow.grow_to_contain(box_fragment.inner_area());
content_area_including_overflow
.grow_to_contain(box_fragment.content_area_including_overflow());
cursor.y += box_fragment.outer_area().height();
children.push(Fragment::Box(box_fragment));
}
Expand All @@ -287,7 +288,7 @@ impl BlockLevelBox {

for fragment in &fragments {
content_area_including_overflow
.grow_to_contain(fragment.content_area_including_overflow())
.grow_to_contain(fragment.content_area_including_overflow());
}

children.extend_from_slice(&fragments);
Expand Down Expand Up @@ -318,6 +319,7 @@ impl BlockLevelBox {
};

BoxFragment::new(
self.node.clone(),
self.style(),
margin,
content_area,
Expand Down

0 comments on commit 4cdea78

Please sign in to comment.