use alloc::vec;
use alloc::vec::Vec;
use crate::components::scroll::ScrollOffset;
use crate::ecs::{Entity, World};
use crate::layout::{LayoutNode, compute_layout};
use crate::types::{Fixed, Rect};
use crate::widget::{Children, Style, Widget};
fn build_rects(
world: &World,
entity: Entity,
parent_node: &mut LayoutNode,
entities: &mut Vec<Entity>,
) {
entities.push(entity);
if let Some(children) = world.get::<Children>(entity) {
for &child in &children.0 {
if world.get::<Widget>(child).is_none() {
continue;
}
if let Some(style) = world.get::<Style>(child) {
let mut child_node = LayoutNode::new(style.layout);
build_rects(world, child, &mut child_node, entities);
parent_node.add_child(child_node);
}
}
}
}
fn collect_rects(node: &LayoutNode, rects: &mut Vec<Rect>) {
rects.push(node.rect);
for child in &node.children {
collect_rects(child, rects);
}
}
fn compute_scroll_offsets(world: &World, root: Entity, entities: &[Entity]) -> Vec<(Fixed, Fixed)> {
let mut offsets = vec![(Fixed::ZERO, Fixed::ZERO); entities.len()];
compute_scroll_recursive(world, root, Fixed::ZERO, Fixed::ZERO, &mut offsets, &mut 0);
offsets
}
fn compute_scroll_recursive(
world: &World,
entity: Entity,
acc_x: Fixed,
acc_y: Fixed,
offsets: &mut [(Fixed, Fixed)],
idx: &mut usize,
) {
if *idx < offsets.len() {
offsets[*idx] = (acc_x, acc_y);
}
*idx += 1;
let (child_acc_x, child_acc_y) = if let Some(scroll) = world.get::<ScrollOffset>(entity) {
(acc_x + scroll.x, acc_y + scroll.y)
} else {
(acc_x, acc_y)
};
if let Some(children) = world.get::<Children>(entity) {
for &child in &children.0 {
if world.get::<Widget>(child).is_some() {
compute_scroll_recursive(world, child, child_acc_x, child_acc_y, offsets, idx);
}
}
}
}
pub fn hit_test(
world: &World,
root: Entity,
x: Fixed,
y: Fixed,
screen_w: u16,
screen_h: u16,
) -> Option<Entity> {
let root_style = world.get::<Style>(root)?;
let mut root_node = LayoutNode::new(root_style.layout);
let mut entities = Vec::new();
build_rects(world, root, &mut root_node, &mut entities);
compute_layout(
&mut root_node,
Fixed::ZERO,
Fixed::ZERO,
screen_w.into(),
screen_h.into(),
);
let mut rects = Vec::new();
collect_rects(&root_node, &mut rects);
let scroll_offsets = compute_scroll_offsets(world, root, &entities);
let mut hit = None;
for (i, rect) in rects.iter().enumerate() {
let (sx, sy) = scroll_offsets[i];
let vx = rect.x - sx;
let vy = rect.y - sy;
let rw = rect.w;
let rh = rect.h;
if x >= vx && x < vx + rw && y >= vy && y < vy + rh {
hit = Some(entities[i]);
}
}
hit
}