Skip to main content

ferroui_base/rendering/composition/
container_visual.rs

1//! `CompositionContainerVisual`: every visual has children, so the members
2//! of the container class are members of [`CompositionVisual`].
3
4use super::hit_testing::{CompositionHitTestAabbTree, ICompositionHitTester};
5use super::{CompositionTarget, CompositionVisual};
6use crate::media::IntersectionResult;
7use std::rc::Rc;
8
9/// A visual that can have children: every visual.
10pub type CompositionContainerVisual = CompositionVisual;
11
12/// The number of children from which the children of a visual are indexed
13/// for hit testing.
14pub(crate) const HIT_TEST_AABB_TREE_THRESHOLD: usize = 32;
15
16impl CompositionVisual {
17    pub(crate) fn add_hit_test_child(&self, child: &Rc<CompositionVisual>) {
18        let mut tree = self.hit_test_children.borrow_mut();
19        let Some(tree) = tree.as_mut() else { return };
20
21        if let Some(order) = self.children().index_of(child) {
22            tree.update(child, order as i32);
23        }
24        Self::update_hit_test_child_order(self, tree);
25    }
26
27    pub(crate) fn remove_hit_test_child(&self, child: &Rc<CompositionVisual>) {
28        let mut tree = self.hit_test_children.borrow_mut();
29        let Some(tree) = tree.as_mut() else { return };
30
31        tree.remove(child);
32        Self::update_hit_test_child_order(self, tree);
33    }
34
35    pub(crate) fn clear_hit_test_children(&self) {
36        if let Some(tree) = self.hit_test_children.borrow_mut().as_mut() {
37            tree.clear();
38        }
39    }
40
41    /// Collects the children that may be hit by `input`, topmost first,
42    /// using the hit test index. Returns `false` when the children are not
43    /// indexed (there are too few of them).
44    pub(crate) fn try_query_hit_test_children<H: ICompositionHitTester>(
45        &self,
46        input: &H::Input,
47        results: &mut Vec<Rc<CompositionVisual>>,
48    ) -> bool {
49        if self.children().count() < HIT_TEST_AABB_TREE_THRESHOLD {
50            *self.hit_test_children.borrow_mut() = None;
51            return false;
52        }
53
54        let read_revision = self.compositor().readback().read_revision();
55        let mut tree = self.hit_test_children.borrow_mut();
56        let tree = tree.get_or_insert_with(|| CompositionHitTestAabbTree::new(self.children().clone()));
57        tree.query::<H>(input, results, read_revision);
58        true
59    }
60
61    /// Finds the topmost hit among the children using the hit test index.
62    /// Returns `None` when the children are not indexed, otherwise the hit
63    /// (if any) and its intersection result.
64    pub(crate) fn try_query_first_hit_test_child<H: ICompositionHitTester>(
65        &self,
66        target: &CompositionTarget,
67        input: &H::Input,
68        filter: Option<&dyn Fn(&Rc<CompositionVisual>) -> bool>,
69        result_filter: Option<&dyn Fn(&Rc<CompositionVisual>) -> bool>,
70    ) -> Option<(Option<Rc<CompositionVisual>>, IntersectionResult)> {
71        if self.children().count() < HIT_TEST_AABB_TREE_THRESHOLD {
72            *self.hit_test_children.borrow_mut() = None;
73            return None;
74        }
75
76        let read_revision = self.compositor().readback().read_revision();
77        let mut tree = self.hit_test_children.borrow_mut();
78        let tree = tree.get_or_insert_with(|| CompositionHitTestAabbTree::new(self.children().clone()));
79        Some(tree.query_first::<H>(target, input, filter, result_filter, read_revision))
80    }
81
82    fn update_hit_test_child_order(&self, tree: &mut CompositionHitTestAabbTree) {
83        for (i, child) in self.children().items().iter().enumerate() {
84            tree.update_order(child, i as i32);
85        }
86    }
87}