orinium_browser/engine/input/
mod.rs

1//! 入力処理とヒットテスト。クリック位置の要素判定を行う。
2
3use super::layouter::types::{InfoNode, NodeKind};
4use ui_layout::LayoutNode;
5
6/// ヒットしたノード情報
7pub struct HitItem<'a> {
8    pub layout: &'a LayoutNode,
9    pub info: &'a InfoNode,
10}
11
12/// ヒットパス(子→親の順)
13pub type HitPath<'a> = Vec<HitItem<'a>>;
14
15/// x, y: グローバル座標
16pub fn hit_test<'a>(layout: &'a LayoutNode, info: &'a InfoNode, x: f32, y: f32) -> HitPath<'a> {
17    // layout_boxes が空なら何もヒットしない
18    if layout.layout_boxes.is_empty() {
19        return Vec::new();
20    }
21
22    for box_model in layout
23        .layout_boxes
24        .iter()
25        .collect::<Vec<_>>()
26        .into_iter()
27        .rev()
28    {
29        // 後ろの box が前面
30        let rect = box_model.padding_box;
31
32        // 1. rect 外なら次の box へ
33        if x < rect.x || y < rect.y || x > rect.x + rect.width || y > rect.y + rect.height {
34            continue;
35        }
36
37        // 2. ローカル座標に変換(スクロールオフセット考慮)
38        let mut local_x = x - box_model.content_box.x;
39        let mut local_y = y - box_model.content_box.y;
40
41        if let NodeKind::Container {
42            scroll_offset_x,
43            scroll_offset_y,
44            ..
45        } = &info.kind
46        {
47            local_x += *scroll_offset_x;
48            local_y += *scroll_offset_y;
49        }
50
51        // 3. 子ノードを前面から探索
52        for (child_layout, child_info) in layout.children.iter().zip(&info.children).rev() {
53            let mut path = hit_test(child_layout, child_info, local_x, local_y);
54            if !path.is_empty() {
55                // 子がヒット → 自分を末尾に追加
56                path.push(HitItem { layout, info });
57                return path;
58            }
59        }
60
61        // 4. 子ノードに当たらなければこの box がヒット
62        return vec![HitItem { layout, info }];
63    }
64
65    // どの box にもヒットしなかった
66    Vec::new()
67}