Skip to main content

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

1use super::ServerCompositionVisual;
2use crate::platform::LtrbRect;
3use crate::rendering::composition::MatrixUtils;
4use crate::Matrix;
5
6impl ServerCompositionVisual {
7    pub fn own_transform(&self) -> Option<Matrix> {
8        self.own_transform.get()
9    }
10
11    pub fn sub_tree_bounds(&self) -> Option<LtrbRect> {
12        self.sub_tree_bounds.get()
13    }
14
15    pub fn transformed_sub_tree_bounds(&self) -> Option<LtrbRect> {
16        self.transformed_sub_tree_bounds.get()
17    }
18
19    pub fn compute_own_content_bounds(&self) -> Option<LtrbRect> {
20        self.content.compute_own_content_bounds(self)
21    }
22
23    pub fn combined_transform_matrix(&self) -> Matrix {
24        self.combined_transform_matrix.get()
25    }
26
27    // WPF's cheatsheet
28    //-----------------------------------------------------------------------------
29    //  Node Operation   | NeedsToBe     | NeedsBBoxUpdate | HasNodeThat    | Visit
30    //                   | AddedToDirty  | (parent chain)  | NeedsToBeAdded | child
31    //                   | Region        |                 | ToDirtyRegion  |
32    //                   |               |                 | (parent chain) |
33    //=============================================================================
34    //  Set transform    |   Y           |   Y             |   Y(N)
35    //  -----------------+---------------+-----------------+-----------------------
36    //  Set opacity      |   Y           |   N             |   Y(N)
37    //  -----------------+---------------+-----------------+-----------------------
38    //  Set clip         |   Y           |   Y             |   Y(N)
39    //  -----------------+---------------+-----------------+-----------------------
40    //  AttachRenderData |   Y           |   Y             |   Y(N)
41    //  -----------------+---------------+-----------------+-----------------------
42    //  FreeRenderData   |   Y           |   Y             |   Y(N)
43    //  -----------------+---------------+-----------------+-----------------------
44    //  InsertChild      |   N           |   Y             |   Y
45    //                   |   Y(child)    |   N             |   Y(N)
46    //  -----------------+---------------+-----------------+-----------------------
47    //  InsertChildAt    |   N           |   Y             |   Y
48    //                   |   Y(child)    |   N             |   Y(N)
49    //  -----------------+---------------+-----------------+-----------------------
50    //  ZOrderChild      |   N           |   N             |   Y
51    //                   |   Y(child)    |   N             |   Y(N)
52    //  -----------------+---------------+-----------------+-----------------------
53    //  ReplaceChild     |   Y           |   Y             |   Y(N)
54    //  -----------------+---------------+-----------------+-----------------------
55    //  RemoveChild      |   Y           |   Y             |   Y(N)
56    pub(super) fn propagate_flags(
57        &self,
58        needs_bounding_box_update: bool,
59        dirty_for_render: bool,
60        additional_dirty_region: bool,
61    ) {
62        if let Some(root) = self.root() {
63            root.request_update();
64        }
65
66        let mut parent = self.parent();
67        let set_is_dirty_for_render_in_subgraph = additional_dirty_region || dirty_for_render;
68        while let Some(p) = parent {
69            if !((needs_bounding_box_update && !p.needs_bounding_box_update.get())
70                || (set_is_dirty_for_render_in_subgraph && !p.is_dirty_for_render_in_subgraph.get()))
71            {
72                break;
73            }
74            p.needs_bounding_box_update.set(p.needs_bounding_box_update.get() | needs_bounding_box_update);
75            p.is_dirty_for_render_in_subgraph
76                .set(p.is_dirty_for_render_in_subgraph.get() | set_is_dirty_for_render_in_subgraph);
77            parent = p.parent();
78        }
79
80        self.needs_bounding_box_update.set(self.needs_bounding_box_update.get() | needs_bounding_box_update);
81        self.is_dirty_for_render.set(self.is_dirty_for_render.get() | dirty_for_render);
82
83        // If node itself is dirty for render, we don't need to keep track of extra dirty rects
84        self.needs_to_add_extra_dirty_rect_to_dirty_region.set(
85            !dirty_for_render && (self.needs_to_add_extra_dirty_rect_to_dirty_region.get() || additional_dirty_region),
86        );
87    }
88
89    pub fn recompute_own_properties(&self) {
90        let mut set_dirty_bounds = self.content_changed.get() || self.delay_propagate_needs_bounds_update.get();
91        let mut set_dirty_for_render = self.content_changed.get() || self.delay_propagate_is_dirty_for_render.get();
92        let set_has_extra_dirty_rect = self.delay_propagate_has_extra_dirty_rects.get();
93
94        // As upstream: the bounds-update flag is not part of this reset.
95        self.delay_propagate_is_dirty_for_render.set(false);
96        self.delay_propagate_has_extra_dirty_rects.set(false);
97
98        self.enqueued_for_own_properties_recompute.set(false);
99        if self.own_bounds_dirty.get() {
100            self.own_content_bounds
101                .set(self.compute_own_content_bounds().and_then(LtrbRect::null_if_zero_size));
102            set_dirty_for_render = true;
103            set_dirty_bounds = true;
104        }
105
106        if self.clip_size_dirty.get() {
107            let mut clip: Option<LtrbRect> = None;
108            if let Some(geometry) = self.clip() {
109                clip = Some(LtrbRect::from_rect(geometry.bounds()));
110            }
111            if self.clip_to_bounds() {
112                let size = self.size();
113                let bounds = LtrbRect::new(0.0, 0.0, size.x, size.y);
114                clip = Some(match clip {
115                    Some(clip) => clip.intersect_or_empty(bounds),
116                    None => bounds,
117                });
118            }
119
120            if self.own_clip_rect.get() != clip {
121                self.own_clip_rect.set(clip);
122                set_dirty_for_render = true;
123                set_dirty_bounds = true;
124            }
125        }
126
127        if self.combined_transform_dirty.get() {
128            self.own_transform.set(MatrixUtils::compute_transform(
129                self.size(),
130                self.anchor_point(),
131                self.center_point(),
132                self.transform_matrix(),
133                self.scale(),
134                self.rotation_angle(),
135                self.orientation(),
136                self.offset() + self.translation(),
137            ));
138
139            set_dirty_for_render = true;
140            set_dirty_bounds = true;
141
142            self.att_helper_combined_transform_changed();
143        }
144
145        set_dirty_for_render |= self.composition_fields_dirty.get();
146
147        self.own_bounds_dirty.set(false);
148        self.clip_size_dirty.set(false);
149        self.combined_transform_dirty.set(false);
150        self.composition_fields_dirty.set(false);
151        self.propagate_flags(set_dirty_bounds, set_dirty_for_render, set_has_extra_dirty_rect);
152    }
153}