ferroui_base/rendering/composition/server/server_composition_visual/
adorners.rs1use 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 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 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 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 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 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 || 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}