Skip to main content

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

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