Skip to main content

ferroui_base/rendering/composition/server/server_composition_visual/
walker.rs

1use super::ServerCompositionVisual;
2use crate::platform::LtrbRect;
3use crate::rendering::composition::server::CompositorPools;
4use crate::Matrix;
5use std::rc::Rc;
6
7pub(super) trait IServerTreeVisitor {
8    /// Returns whether the children are to be visited.
9    fn pre_subgraph(&mut self, visual: &Rc<ServerCompositionVisual>) -> bool;
10    fn post_subgraph(&mut self, visual: &Rc<ServerCompositionVisual>);
11}
12
13/// A position of the tree walk: a container and the index of its child to
14/// visit next.
15pub struct TreeWalkerFrame {
16    visual: Rc<ServerCompositionVisual>,
17    children: Rc<Vec<Rc<ServerCompositionVisual>>>,
18    current_index: usize,
19}
20
21/// The children of a visual, `None` for a visual without a children
22/// collection (the original reads `Children.List.Count` of both alike, and
23/// nothing is allocated for such a visual).
24fn children_of(visual: &ServerCompositionVisual) -> Option<Rc<Vec<Rc<ServerCompositionVisual>>>> {
25    visual.children().map(|children| children.list())
26}
27
28pub(super) fn walk<V: IServerTreeVisitor>(visitor: &mut V, root: &Rc<ServerCompositionVisual>, pools: &CompositorPools) {
29    let mut frames = pools.tree_walker_frame_stack_pool.rent();
30
31    let visit_children = visitor.pre_subgraph(root);
32    let mut container = root.clone();
33    let mut children = match children_of(&container) {
34        Some(children) if visit_children && !children.is_empty() => children,
35        _ => {
36            visitor.post_subgraph(root);
37            pools.tree_walker_frame_stack_pool.return_stack(frames);
38            return;
39        }
40    };
41
42    let mut current_index = 0usize;
43
44    loop {
45        if current_index >= children.len() {
46            // Exiting "recursion"
47            visitor.post_subgraph(&container);
48            let Some(frame) = frames.pop() else { break };
49            container = frame.visual;
50            children = frame.children;
51            current_index = frame.current_index;
52            continue;
53        }
54
55        let child = children[current_index].clone();
56        let visit_children = visitor.pre_subgraph(&child);
57        if visit_children {
58            if let Some(child_children) = children_of(&child).filter(|children| !children.is_empty()) {
59                // Go deeper
60                frames.push(TreeWalkerFrame { visual: container, children, current_index: current_index + 1 });
61                container = child;
62                children = child_children;
63                current_index = 0;
64                continue; // Enter "recursion"
65            }
66        }
67
68        // Haven't entered recursion, still call PostSubgraph and go to the next sibling
69        visitor.post_subgraph(&child);
70        current_index += 1;
71    }
72
73    pools.tree_walker_frame_stack_pool.return_stack(frames);
74}
75
76pub(super) struct TreeWalkContext {
77    pub(super) transform: Matrix,
78    pub(super) clip: LtrbRect,
79    transform_stack: Vec<Matrix>,
80    clip_stack: Vec<LtrbRect>,
81}
82
83impl TreeWalkContext {
84    pub(super) fn new(pools: &CompositorPools, transform: Matrix, clip: LtrbRect) -> Self {
85        Self {
86            transform,
87            clip,
88            transform_stack: pools.matrix_stack_pool.rent(),
89            clip_stack: pools.ltrb_rect_stack_pool.rent(),
90        }
91    }
92
93    pub(super) fn push_transform(&mut self, m: Matrix) {
94        self.transform_stack.push(self.transform);
95        self.transform = m * self.transform;
96    }
97
98    pub(super) fn push_set_transform(&mut self, m: Matrix) {
99        self.transform_stack.push(self.transform);
100        self.transform = m;
101    }
102
103    pub(super) fn push_clip(&mut self, rect: LtrbRect) {
104        self.clip_stack.push(self.clip);
105        self.clip = self.clip.intersect_or_empty(rect);
106    }
107
108    pub(super) fn reset_clip(&mut self, rect: LtrbRect) {
109        self.clip_stack.push(self.clip);
110        self.clip = rect;
111    }
112
113    pub(super) fn pop_transform(&mut self) {
114        self.transform = self.transform_stack.pop().expect("a transform was pushed");
115    }
116
117    pub(super) fn pop_clip(&mut self) {
118        self.clip = self.clip_stack.pop().expect("a clip was pushed");
119    }
120
121    pub(super) fn dispose(self, pools: &CompositorPools) {
122        pools.matrix_stack_pool.return_stack(self.transform_stack);
123        pools.ltrb_rect_stack_pool.return_stack(self.clip_stack);
124    }
125}