Skip to main content

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

1//! Support for adorners is a rather invasive thing, so all the related code
2//! is isolated in this file and prefixed with `adorner_helper_`.
3
4use super::render::RenderContext;
5use super::ServerCompositionVisual;
6use crate::rendering::composition::MatrixUtils;
7use crate::{Matrix, Rect};
8use std::rc::Rc;
9
10const OP_POP_CLIP: i32 = 0;
11const OP_POP_GEOMETRY_CLIP: i32 = 1;
12const OP_STOP: i32 = 2;
13
14impl ServerCompositionVisual {
15    pub(super) fn adorner_helper_attached_to_root(&self) {
16        if self.adorned_visual().is_some() {
17            self.adorner_helper_enqueue_for_adorner_update();
18        }
19    }
20
21    pub fn adorner_helper_enqueue_for_adorner_update(&self) {
22        if self.att_helper.enqueued_for_adorner_update.get() {
23            return;
24        }
25        if let (Some(compositor), Some(this)) = (self.compositor(), self.rc()) {
26            compositor.enqueue_adorner_update(this);
27            self.att_helper.enqueued_for_adorner_update.set(true);
28        }
29    }
30
31    pub(super) fn on_adorned_visual_changing_core(&self) {
32        if let Some(adorned) = self.adorned_visual() {
33            adorned.att_helper_unsubscribe_from_act_notification(&self.adorned_visual_act_subscription_action());
34        }
35    }
36
37    pub(super) fn on_adorned_visual_changed_core(&self) {
38        if let Some(adorned) = self.adorned_visual() {
39            adorned.att_helper_subscribe_to_act_notification(self.adorned_visual_act_subscription_action());
40        }
41        self.adorner_helper_enqueue_for_adorner_update();
42    }
43
44    fn adorner_layer_get_expected_shared_ancestor(adorner: &ServerCompositionVisual) -> Option<Rc<ServerCompositionVisual>> {
45        // This is hardcoded to VisualLayerManager -> AdornerLayer -> adorner
46        // Since AdornedVisual is a private API that's only supposed to be accessible from AdornerLayer
47        // it's a safe assumption to make
48        adorner.parent()?.parent()
49    }
50
51    pub fn update_adorner(&self) {
52        self.att_helper.enqueued_for_adorner_update.set(false);
53        let adorned = self.adorned_visual();
54        if let (Some(adorned), true) = (&adorned, self.parent().is_some()) {
55            // We ignore Visual's RenderTransform completely since it's set by AdornerLayer and can be out of sync
56            // with compositor-driver animations
57            let own_transform = MatrixUtils::compute_transform(
58                self.size(),
59                self.anchor_point(),
60                self.center_point(),
61                Matrix::IDENTITY,
62                self.scale(),
63                self.rotation_angle(),
64                self.orientation(),
65                self.offset() + self.translation(),
66            );
67
68            let adorner_layer_to_adorned_visual = Self::adorner_layer_get_expected_shared_ancestor(self)
69                .and_then(|shared_ancestor| Self::compute_transform_from_ancestor(adorned, &shared_ancestor));
70            match adorner_layer_to_adorned_visual {
71                Some(transform) => {
72                    self.own_transform.set(Some(own_transform.unwrap_or(Matrix::IDENTITY) * transform))
73                }
74                // Don't render, something is broken
75                None => self.own_transform.set(Some(Matrix::new_3x3(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0))),
76            }
77        } else if self.parent_for_transform().is_some() {
78            // The WinUI `ParentForTransform` (not from upstream).
79            self.update_parent_for_transform();
80        } else {
81            self.own_transform.set(self.compute_own_transform_from_properties(self.transform_matrix()));
82        }
83
84        self.propagate_flags(true, true, false);
85    }
86}
87
88impl RenderContext<'_> {
89    fn adorner_layer_walk_adorner_parent_clip_recursive(&mut self, visual: Option<Rc<ServerCompositionVisual>>) -> bool {
90        // AdornedVisual is a part of a different subtree, this is not supported
91        let Some(visual) = visual else { return false };
92        let is_layer = self.current_adorner_layer.as_ref().is_some_and(|layer| Rc::ptr_eq(layer, &visual));
93        if !is_layer && !self.adorner_layer_walk_adorner_parent_clip_recursive(visual.parent()) {
94            return false;
95        }
96
97        if let Some(own_transform) = visual.own_transform.get() {
98            let transform = own_transform * self.canvas.transform();
99            self.canvas.set_transform(transform);
100        }
101
102        if visual.clip_to_bounds() {
103            let size = visual.size();
104            self.canvas.push_clip(Rect::new(0.0, 0.0, size.x, size.y));
105            if let Some(stack) = &mut self.adorner_pushed_clip_stack {
106                stack.push(OP_POP_CLIP);
107            }
108        }
109
110        if let Some(clip) = visual.clip() {
111            self.canvas.push_geometry_clip(&*clip);
112            if let Some(stack) = &mut self.adorner_pushed_clip_stack {
113                stack.push(OP_POP_GEOMETRY_CLIP);
114            }
115        }
116
117        true
118    }
119
120    fn skip_adorner_clip(&self, visual: &Rc<ServerCompositionVisual>) -> bool {
121        !visual.adorner_is_clipped()
122            || Rc::ptr_eq(visual, &self.root_visual)
123            // Root visual is AdornerLayer
124            || visual.parent().is_some_and(|parent| Rc::ptr_eq(&parent, &self.root_visual))
125            || ServerCompositionVisual::adorner_layer_get_expected_shared_ancestor(visual).is_none()
126    }
127
128    pub(super) fn adorner_helper_render_pre_graph_push_adorner_clip(&mut self, visual: &Rc<ServerCompositionVisual>) {
129        if self.skip_adorner_clip(visual) {
130            return;
131        }
132
133        let pools = self.pools;
134        self.adorner_pushed_clip_stack.get_or_insert_with(|| pools.int_stack_pool.rent()).push(OP_STOP);
135
136        let original_transform = self.canvas.transform();
137        let mut transform = original_transform;
138        if let Some(own_transform) = visual.own_transform.get() {
139            let Some(transform_to_adorner_layer) = own_transform.try_invert() else { return };
140            transform = transform_to_adorner_layer * transform;
141        }
142
143        self.canvas.set_transform(transform);
144
145        self.current_adorner_layer = ServerCompositionVisual::adorner_layer_get_expected_shared_ancestor(visual);
146        self.adorner_layer_walk_adorner_parent_clip_recursive(visual.adorned_visual());
147
148        self.canvas.set_transform(original_transform);
149    }
150
151    pub(super) fn adorner_helper_render_post_graph_push_adorner_clip(&mut self, visual: &Rc<ServerCompositionVisual>) {
152        if self.skip_adorner_clip(visual) {
153            return;
154        }
155
156        loop {
157            let Some(op) = self.adorner_pushed_clip_stack.as_mut().and_then(Vec::pop) else { break };
158            if op == OP_STOP {
159                break;
160            }
161            if op == OP_POP_GEOMETRY_CLIP {
162                self.canvas.pop_geometry_clip();
163            } else if op == OP_POP_CLIP {
164                self.canvas.pop_clip();
165            }
166        }
167    }
168
169    pub(super) fn adorner_helper_dispose(&mut self) {
170        if let Some(stack) = self.adorner_pushed_clip_stack.take() {
171            self.pools.int_stack_pool.return_stack(stack);
172        }
173    }
174}