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 {
78            self.own_transform.set(MatrixUtils::compute_transform(
79                self.size(),
80                self.anchor_point(),
81                self.center_point(),
82                self.transform_matrix(),
83                self.scale(),
84                self.rotation_angle(),
85                self.orientation(),
86                self.offset() + self.translation(),
87            ));
88        }
89
90        self.propagate_flags(true, true, false);
91    }
92}
93
94impl RenderContext<'_> {
95    fn adorner_layer_walk_adorner_parent_clip_recursive(&mut self, visual: Option<Rc<ServerCompositionVisual>>) -> bool {
96        // AdornedVisual is a part of a different subtree, this is not supported
97        let Some(visual) = visual else { return false };
98        let is_layer = self.current_adorner_layer.as_ref().is_some_and(|layer| Rc::ptr_eq(layer, &visual));
99        if !is_layer && !self.adorner_layer_walk_adorner_parent_clip_recursive(visual.parent()) {
100            return false;
101        }
102
103        if let Some(own_transform) = visual.own_transform.get() {
104            let transform = own_transform * self.canvas.transform();
105            self.canvas.set_transform(transform);
106        }
107
108        if visual.clip_to_bounds() {
109            let size = visual.size();
110            self.canvas.push_clip(Rect::new(0.0, 0.0, size.x, size.y));
111            if let Some(stack) = &mut self.adorner_pushed_clip_stack {
112                stack.push(OP_POP_CLIP);
113            }
114        }
115
116        if let Some(clip) = visual.clip() {
117            self.canvas.push_geometry_clip(&*clip);
118            if let Some(stack) = &mut self.adorner_pushed_clip_stack {
119                stack.push(OP_POP_GEOMETRY_CLIP);
120            }
121        }
122
123        true
124    }
125
126    fn skip_adorner_clip(&self, visual: &Rc<ServerCompositionVisual>) -> bool {
127        !visual.adorner_is_clipped()
128            || Rc::ptr_eq(visual, &self.root_visual)
129            // Root visual is AdornerLayer
130            || visual.parent().is_some_and(|parent| Rc::ptr_eq(&parent, &self.root_visual))
131            || ServerCompositionVisual::adorner_layer_get_expected_shared_ancestor(visual).is_none()
132    }
133
134    pub(super) fn adorner_helper_render_pre_graph_push_adorner_clip(&mut self, visual: &Rc<ServerCompositionVisual>) {
135        if self.skip_adorner_clip(visual) {
136            return;
137        }
138
139        let pools = self.pools;
140        self.adorner_pushed_clip_stack.get_or_insert_with(|| pools.int_stack_pool.rent()).push(OP_STOP);
141
142        let original_transform = self.canvas.transform();
143        let mut transform = original_transform;
144        if let Some(own_transform) = visual.own_transform.get() {
145            let Some(transform_to_adorner_layer) = own_transform.try_invert() else { return };
146            transform = transform_to_adorner_layer * transform;
147        }
148
149        self.canvas.set_transform(transform);
150
151        self.current_adorner_layer = ServerCompositionVisual::adorner_layer_get_expected_shared_ancestor(visual);
152        self.adorner_layer_walk_adorner_parent_clip_recursive(visual.adorned_visual());
153
154        self.canvas.set_transform(original_transform);
155    }
156
157    pub(super) fn adorner_helper_render_post_graph_push_adorner_clip(&mut self, visual: &Rc<ServerCompositionVisual>) {
158        if self.skip_adorner_clip(visual) {
159            return;
160        }
161
162        loop {
163            let Some(op) = self.adorner_pushed_clip_stack.as_mut().and_then(Vec::pop) else { break };
164            if op == OP_STOP {
165                break;
166            }
167            if op == OP_POP_GEOMETRY_CLIP {
168                self.canvas.pop_geometry_clip();
169            } else if op == OP_POP_CLIP {
170                self.canvas.pop_clip();
171            }
172        }
173    }
174
175    pub(super) fn adorner_helper_dispose(&mut self) {
176        if let Some(stack) = self.adorner_pushed_clip_stack.take() {
177            self.pools.int_stack_pool.return_stack(stack);
178        }
179    }
180}