ferroui_base/rendering/composition/server/server_composition_visual/
winui_members.rs1use super::act::ActSubscriber;
7use super::ServerCompositionVisual;
8use crate::media::EffectExtensions;
9use crate::numerics::{Quaternion, Vector3};
10use crate::platform::LtrbRect;
11use crate::rendering::composition::generated::ServerCompositionVisualProps;
12use crate::rendering::composition::{
13 CompositionBackfaceVisibility, CompositionBorderMode, CompositionCompositeMode, MatrixUtils,
14};
15use crate::{Matrix, Vector, Vector3D};
16use std::rc::Rc;
17
18impl ServerCompositionVisual {
19 pub fn rotation_axis(&self) -> Vector3D {
23 self.props.base().rotation_axis()
24 }
25
26 pub fn relative_offset_adjustment(&self) -> Vector3D {
28 self.props.base().relative_offset_adjustment()
29 }
30
31 pub fn relative_size_adjustment(&self) -> Vector {
33 self.props.base().relative_size_adjustment()
34 }
35
36 pub fn is_hit_test_visible(&self) -> bool {
37 self.props.base().is_hit_test_visible()
38 }
39
40 pub fn is_pixel_snapping_enabled(&self) -> bool {
41 self.props.base().is_pixel_snapping_enabled()
42 }
43
44 pub fn border_mode(&self) -> CompositionBorderMode {
45 self.props.base().border_mode()
46 }
47
48 pub fn backface_visibility(&self) -> CompositionBackfaceVisibility {
49 self.props.base().backface_visibility()
50 }
51
52 pub fn composite_mode(&self) -> CompositionCompositeMode {
53 self.props.base().composite_mode()
54 }
55
56 pub fn parent_for_transform(&self) -> Option<Rc<ServerCompositionVisual>> {
59 self.props
60 .base()
61 .parent_for_transform()
62 .and_then(|visual| visual.into_any_rc().downcast::<ServerCompositionVisual>().ok())
63 }
64
65 pub(super) fn effective_size(&self) -> Vector {
70 let size = self.props.base().size();
71 let relative = self.relative_size_adjustment();
72 if relative == Vector::default() {
73 return size;
74 }
75 let parent = self.parent().map(|parent| parent.size()).unwrap_or_default();
76 size + Vector::multiply(relative, parent)
77 }
78
79 pub(super) fn effective_offset(&self) -> Vector3D {
82 let offset = self.props.base().offset();
83 let relative = self.relative_offset_adjustment();
84 if relative.x == 0.0 && relative.y == 0.0 {
85 return offset;
86 }
87 let parent = self.parent().map(|parent| parent.size()).unwrap_or_default();
88 offset + Vector3D::new(relative.x * parent.x, relative.y * parent.y, 0.0)
89 }
90
91 fn follows_parent_size(&self) -> (bool, bool) {
94 let offset = self.relative_offset_adjustment();
95 (offset.x != 0.0 || offset.y != 0.0, self.relative_size_adjustment() != Vector::default())
96 }
97
98 pub(super) fn notify_children_of_size_change(&self) {
102 let Some(children) = self.children() else { return };
103 for child in children.list().iter() {
104 child.parent_size_changed();
105 }
106 }
107
108 pub(super) fn parent_size_changed(&self) {
110 let (offset_follows, size_follows) = self.follows_parent_size();
111 if size_follows {
112 self.effective_size_changed();
113 } else if offset_follows {
114 self.trigger_combined_transform_dirty();
115 }
116 }
117
118 fn effective_size_changed(&self) {
121 self.trigger_composition_fields_dirty();
122 self.trigger_combined_transform_dirty();
123 self.trigger_clip_size_dirty();
124 self.own_bounds_dirty.set(true);
125 self.content.size_changed(self);
126 self.notify_children_of_size_change();
127 }
128
129 fn effective_rotation(&self) -> (f32, Quaternion) {
136 let angle = self.rotation_angle();
137 let orientation = self.orientation();
138 let axis = self.rotation_axis();
139 if angle == 0.0 || (axis.x == 0.0 && axis.y == 0.0) {
140 let angle = if axis.z < 0.0 { -angle } else { angle };
141 return (angle, orientation);
142 }
143 let axis = Vector3::normalize(axis.into());
144 let rotation = Quaternion::create_from_axis_angle(axis, angle);
145 (0.0, Quaternion::concatenate(rotation, orientation))
146 }
147
148 pub fn depth_offset(&self, z: f32) -> Vector {
163 if z == 0.0 || !z.is_finite() {
164 return Vector::default();
165 }
166 let (_, orientation) = self.effective_rotation();
167 if orientation == Quaternion::IDENTITY {
168 return Vector::default();
169 }
170 let rotation = crate::numerics::Matrix4x4::create_from_quaternion(orientation);
171 let depth = (z * self.scale().z as f32) as f64;
172 let moved = Vector::new(depth * rotation.m31 as f64, depth * rotation.m32 as f64);
173 if moved == Vector::default() {
174 return moved;
175 }
176 let own = self.own_transform.get().unwrap_or(Matrix::IDENTITY);
179 let linear = Matrix::new(own.m11, own.m12, own.m21, own.m22, 0.0, 0.0);
180 match linear.try_invert() {
181 Some(inverse) => {
182 let local = crate::Point::new(moved.x, moved.y).transform(inverse);
183 Vector::new(local.x, local.y)
184 }
185 None => Vector::default(),
187 }
188 }
189
190 pub(super) fn compute_own_transform_from_properties(&self, transform_matrix: Matrix) -> Option<Matrix> {
194 let (rotation_angle, orientation) = self.effective_rotation();
195 let transform = MatrixUtils::compute_transform(
196 self.size(),
197 self.anchor_point(),
198 self.center_point(),
199 transform_matrix,
200 self.scale(),
201 rotation_angle,
202 orientation,
203 self.offset() + self.translation(),
204 );
205 if !self.is_pixel_snapping_enabled() {
206 return transform;
207 }
208 let scaling = self.root().map(|root| root.scaling()).filter(|scaling| *scaling > 0.0).unwrap_or(1.0);
211 let mut snapped = transform.unwrap_or(Matrix::IDENTITY);
212 snapped.m31 = (snapped.m31 * scaling).round() / scaling;
213 snapped.m32 = (snapped.m32 * scaling).round() / scaling;
214 (!snapped.is_identity()).then_some(snapped)
215 }
216
217 fn parent_for_transform_matrix(&self) -> Option<Matrix> {
221 let target = self.parent_for_transform()?;
222 let parent = self.parent()?;
223 if Rc::ptr_eq(&target, &parent) {
224 return Some(Matrix::IDENTITY);
225 }
226 let mut target_chain = Vec::new();
227 let mut visual = Some(target.clone());
228 while let Some(current) = visual {
229 visual = current.parent();
230 target_chain.push(current);
231 }
232 let mut candidate = Some(parent.clone());
233 while let Some(ancestor) = candidate {
234 if target_chain.iter().any(|visual| Rc::ptr_eq(visual, &ancestor)) {
235 let target_to_ancestor = if Rc::ptr_eq(&target, &ancestor) {
236 Matrix::IDENTITY
237 } else {
238 Self::compute_transform_from_ancestor(&target, &ancestor)?
239 };
240 let parent_to_ancestor = if Rc::ptr_eq(&parent, &ancestor) {
241 Matrix::IDENTITY
242 } else {
243 Self::compute_transform_from_ancestor(&parent, &ancestor)?
244 };
245 return Some(target_to_ancestor * parent_to_ancestor.try_invert()?);
246 }
247 candidate = ancestor.parent();
248 }
249 None
250 }
251
252 pub(super) fn update_parent_for_transform(&self) {
256 let own = self.compute_own_transform_from_properties(self.transform_matrix());
257 let transform = match self.parent_for_transform_matrix() {
258 Some(relative) => {
259 let transform = own.unwrap_or(Matrix::IDENTITY) * relative;
260 (!transform.is_identity()).then_some(transform)
261 }
262 None => own,
263 };
264 self.own_transform.set(transform);
265 }
266
267 fn parent_for_transform_subscription(&self) -> ActSubscriber {
268 self.adorned_visual_act_subscription_action()
269 }
270
271 fn subscribe_parent_for_transform(&self, subscribe: bool) {
274 let Some(target) = self.parent_for_transform() else { return };
275 let subscription = self.parent_for_transform_subscription();
276 let mut followed = vec![target];
277 if let Some(parent) = self.parent() {
278 followed.push(parent);
279 }
280 for visual in followed {
281 if visual.disposed.get() {
282 continue;
283 }
284 if subscribe {
285 visual.att_helper_subscribe_to_act_notification(subscription.clone());
286 } else {
287 visual.att_helper_unsubscribe_from_act_notification(&subscription);
288 }
289 }
290 }
291
292 pub(super) fn on_parent_for_transform_changing_core(&self) {
293 self.subscribe_parent_for_transform(false);
294 }
295
296 pub(super) fn on_parent_for_transform_changed_core(&self) {
297 self.subscribe_parent_for_transform(true);
298 self.trigger_combined_transform_dirty();
299 self.adorner_helper_enqueue_for_adorner_update();
300 }
301
302 pub(super) fn winui_members_parent_changing(&self) {
304 if self.parent_for_transform().is_some() {
305 if let Some(parent) = self.parent() {
306 parent.att_helper_unsubscribe_from_act_notification(&self.parent_for_transform_subscription());
307 }
308 }
309 }
310
311 pub(super) fn winui_members_parent_changed(&self) {
314 if self.parent_for_transform().is_some() {
315 if let Some(parent) = self.parent() {
316 parent.att_helper_subscribe_to_act_notification(self.parent_for_transform_subscription());
317 }
318 self.adorner_helper_enqueue_for_adorner_update();
319 }
320 if self.parent().is_some() {
321 self.parent_size_changed();
322 }
323 }
324
325 pub(super) fn winui_members_dispose(&self) {
328 self.subscribe_parent_for_transform(false);
329 ServerCompositionVisualProps::id_of_parent_for_transform_property().set_field(self, None);
330 }
331
332 pub fn unclipped_sub_tree_bounds(&self) -> Option<LtrbRect> {
340 let mut bounds = self.own_content_bounds.get();
341 if let Some(children) = self.children() {
342 for child in children.list().iter() {
343 bounds = LtrbRect::full_union(bounds, child.transformed_sub_tree_bounds.get());
344 }
345 }
346 match (bounds, self.effect()) {
347 (Some(bounds), Some(effect)) => {
348 Some(bounds.inflate(EffectExtensions::get_effect_output_padding(Some(&*effect))))
349 }
350 (bounds, _) => bounds,
351 }
352 }
353
354 pub fn is_self_or_descendant_of(&self, ancestor: &ServerCompositionVisual) -> bool {
356 if std::ptr::eq(self, ancestor) {
357 return true;
358 }
359 let mut visual = self.parent();
360 while let Some(current) = visual {
361 if std::ptr::eq(&*current, ancestor) {
362 return true;
363 }
364 visual = current.parent();
365 }
366 false
367 }
368}