1use crate::collections::{FerroList, NotifyCollectionChangedAction, NotifyCollectionChangedEventArgs, ResetBehavior};
2use crate::media::{
3 can_value_affect_render, subscribe_invalidated, CacheMode, DrawingContext, FlowDirection, Geometry, IBrush, IEffect,
4 ITransform, RenderOptions, TextOptions, Transform,
5};
6use crate::rendering::composition::{CompositionDrawListVisual, CompositionVisual, Compositor};
7use crate::reactive::{Disposable, IDisposable};
8use crate::rendering::IPresentationSource;
9use crate::utilities::HandlerList;
10use crate::{
11 ferro_class, ferro_property, instantiate, AttachedProperty, DirectProperty, FerroObjectImpl,
12 FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, Nullable, ObjectType, Rect, Ref,
13 RelativePoint, StyledElement,
14 StyledElementImpl, StyledElementImplExt, StyledProperty, StyledPropertyOptions, Upcast,
15 VisualTreeAttachmentEventArgs, WeakRef,
16};
17use std::cell::{Cell, OnceCell, RefCell};
18use std::rc::Rc;
19
20#[repr(C)]
28pub struct Visual {
29 base: StyledElement,
30 bounds: Cell<Rect>,
31 presentation_source: RefCell<Option<Rc<dyn IPresentationSource>>>,
32 visual_parent: RefCell<Option<WeakRef<Visual>>>,
33 has_mirror_transform: Cell<bool>,
34 is_effectively_visible: Cell<bool>,
35 has_non_uniform_z_index_children: Cell<bool>,
36 visual_children: OnceCell<FerroList<Ref<Visual>>>,
37 attached_to_visual_tree: HandlerList<dyn Fn(&VisualTreeAttachmentEventArgs)>,
38 detached_from_visual_tree: HandlerList<dyn Fn(&VisualTreeAttachmentEventArgs)>,
39 is_effectively_visible_changed: HandlerList<dyn Fn()>,
40 render_options: Cell<RenderOptions>,
41 text_options: Cell<TextOptions>,
42 composition_visual: RefCell<Option<CompositionDrawListVisual>>,
43 child_composition_visual: RefCell<Option<Rc<CompositionVisual>>>,
44 render_transform_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
47 affects_render_subscriptions: RefCell<Vec<(&'static FerroProperty, Rc<dyn IDisposable>)>>,
48}
49
50ferro_class! {
51 Visual: StyledElement, virtuals VisualImpl: StyledElementImpl {
52 fn render(this, context: &mut DrawingContext);
54 fn on_attached_to_visual_tree_core(this, e: &VisualTreeAttachmentEventArgs);
57 fn on_detached_from_visual_tree_core(this, e: &VisualTreeAttachmentEventArgs);
60 fn on_attached_to_visual_tree(this, e: &VisualTreeAttachmentEventArgs);
62 fn on_detached_from_visual_tree(this, e: &VisualTreeAttachmentEventArgs);
64 fn on_visual_parent_changed(this, old_parent: Option<&Ref<Visual>>, new_parent: Option<&Ref<Visual>>);
66 fn bypass_flow_direction_policies(this) -> bool;
69 fn invalidate_mirror_transform(this);
72 fn clip_to_bounds_radius(this) -> Option<crate::CornerRadius>;
76 fn custom_hit_test(this, point: crate::Point) -> Option<bool>;
81 fn custom_hit_test_geometry(this, geometry: &Ref<crate::media::Geometry>) -> Option<crate::media::IntersectionResult>;
84 fn ensure_initialized_for_visual_brush(this);
88 fn create_composition_visual(this, compositor: &Rc<Compositor>) -> CompositionDrawListVisual;
90 fn detach_from_compositor(this);
92 fn synchronize_composition_child_visuals(this);
95 fn synchronize_composition_properties(this);
97 }
98}
99
100crate::ferro_thread_local! {
101 static ROOTED_VISUAL_CHILDREN_COUNT: Cell<i32> = const { Cell::new(0) };
102}
103crate::ferro_class_info!(Visual { new: Visual::new });
104
105crate::ferro_overrides! { impl FerroObjectImpl for Visual {
106 fn constructed(this: &Self) {
107 Self::parent_constructed(this);
108
109 this.disable_transitions();
111 }
112
113 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
114 Self::parent_on_property_changed(this, change);
115
116 if change.property() == Self::is_visible_property().as_property() {
117 let parent_state = this.visual_parent().is_none_or(|p| p.is_effectively_visible());
118 this.update_is_effectively_visible(parent_state);
119 } else if change.property() == Self::flow_direction_property().as_property() {
120 this.invalidate_mirror_transform();
121 if let Some(children) = this.visual_children.get() {
122 for child in children.snapshot().iter() {
123 child.invalidate_mirror_transform();
124 }
125 }
126 }
127 }
128} }
129
130impl StyledElementImpl for Visual {
131 fn logical_children_collection_changed(this: &Self, e: &NotifyCollectionChangedEventArgs<'_, Ref<StyledElement>>) {
132 Self::parent_logical_children_collection_changed(this, e);
133 if let Some(source) = this.presentation_source() {
134 source.renderer().recalculate_children(this);
135 }
136 }
137
138 fn on_templated_parent_control_theme_changed(this: &Self) {
139 Self::parent_on_templated_parent_control_theme_changed(this);
140 let templated_parent = this.templated_parent();
141 if let Some(children) = this.visual_children.get() {
142 for child in children.snapshot().iter() {
143 if child.templated_parent() == templated_parent {
144 child.on_templated_parent_control_theme_changed();
145 }
146 }
147 }
148 }
149}
150
151impl VisualImpl for Visual {
152 fn render(_this: &Self, _context: &mut DrawingContext) {}
153
154 fn on_attached_to_visual_tree_core(this: &Self, e: &VisualTreeAttachmentEventArgs) {
155 *this.presentation_source.borrow_mut() = Some(e.presentation_source().clone());
156 ROOTED_VISUAL_CHILDREN_COUNT.with(|count| count.set(count.get() + 1));
157
158 if let Some(render_transform) = this.render_transform() {
159 if let Some(mutable_transform) = render_transform.as_mutable_transform() {
160 let weak = this.to_ref().downgrade();
161 let subscription = mutable_transform.changed(Rc::new(move || {
162 if let Some(visual) = weak.upgrade() {
163 visual.render_transform_value_changed();
164 }
165 }));
166 this.set_render_transform_subscription(Some(subscription));
167 }
168 }
169
170 this.enable_transitions();
171 let renderer = e.presentation_source().renderer();
172 if let Some(compositor) = renderer.compositor() {
173 this.attach_to_compositor(&compositor);
174 }
175
176 this.invalidate_mirror_transform();
177 let parent = this.visual_parent();
178 this.update_is_effectively_visible(parent.as_ref().is_none_or(|p| p.is_effectively_visible()));
179 this.on_attached_to_visual_tree(e);
180 if !this.attached_to_visual_tree.is_empty() {
181 for (_, handler) in this.attached_to_visual_tree.snapshot().iter() {
182 handler(e);
183 }
184 }
185 this.invalidate_visual();
186
187 if let Some(parent) = &parent {
188 renderer.recalculate_children(parent);
189 if this.z_index() != 0 {
190 parent.has_non_uniform_z_index_children.set(true);
191 }
192 }
193
194 if let Some(children) = this.visual_children.get() {
195 let this_ref = this.to_ref();
196 for child in children.snapshot().iter() {
197 let attached_here = child
201 .presentation_source()
202 .is_some_and(|s| Rc::ptr_eq(&s, e.presentation_source()));
203 if !attached_here && child.visual_parent().is_some_and(|p| p == this_ref) {
204 child.on_attached_to_visual_tree_core(e);
205 }
206 }
207 }
208 }
209
210 fn on_detached_from_visual_tree_core(this: &Self, e: &VisualTreeAttachmentEventArgs) {
211 ROOTED_VISUAL_CHILDREN_COUNT.with(|count| count.set(count.get() - 1));
212
213 this.set_render_transform_subscription(None);
214
215 this.disable_transitions();
216 this.update_is_effectively_visible(true);
217 this.on_detached_from_visual_tree(e);
218 this.detach_from_compositor();
219 if !this.detached_from_visual_tree.is_empty() {
220 for (_, handler) in this.detached_from_visual_tree.snapshot().iter() {
221 handler(e);
222 }
223 }
224 if let Some(source) = this.presentation_source() {
225 source.renderer().add_dirty(this);
226 }
227 *this.presentation_source.borrow_mut() = None;
228
229 if let Some(children) = this.visual_children.get() {
230 for child in children.snapshot().iter() {
231 if child.presentation_source().is_some() {
234 child.on_detached_from_visual_tree_core(e);
235 }
236 }
237 }
238 }
239
240 fn on_attached_to_visual_tree(_this: &Self, _e: &VisualTreeAttachmentEventArgs) {}
241
242 fn on_detached_from_visual_tree(_this: &Self, _e: &VisualTreeAttachmentEventArgs) {}
243
244 fn on_visual_parent_changed(this: &Self, old_parent: Option<&Ref<Visual>>, new_parent: Option<&Ref<Visual>>) {
245 this.raise_direct_property_changed(
246 Self::visual_parent_property(),
247 &old_parent.cloned(),
248 &new_parent.cloned(),
249 );
250 }
251
252 fn bypass_flow_direction_policies(_this: &Self) -> bool {
253 false
254 }
255
256 fn clip_to_bounds_radius(_this: &Self) -> Option<crate::CornerRadius> {
257 None
258 }
259
260 fn custom_hit_test(_this: &Self, _point: crate::Point) -> Option<bool> {
261 None
262 }
263
264 fn custom_hit_test_geometry(
265 _this: &Self,
266 _geometry: &Ref<crate::media::Geometry>,
267 ) -> Option<crate::media::IntersectionResult> {
268 None
269 }
270
271 fn ensure_initialized_for_visual_brush(_this: &Self) {}
272
273 fn create_composition_visual(this: &Self, compositor: &Rc<Compositor>) -> CompositionDrawListVisual {
274 CompositionDrawListVisual::new(compositor, this)
275 }
276
277 fn detach_from_compositor(this: &Self) {
278 let composition_visual = this.composition_visual.borrow_mut().take();
279 if let Some(composition_visual) = composition_visual {
280 if let Some(child) = this.child_composition_visual() {
281 composition_visual.children().remove(&child);
282 }
283 composition_visual.set_draw_list(None);
284 composition_visual.set_opacity_mask(None);
285 composition_visual.children().clear();
290 }
291 }
292
293 fn synchronize_composition_child_visuals(this: &Self) {
294 let Some(composition_visual) = this.composition_visual() else { return };
295 let composition_children = composition_visual.children();
296 let visual_children: Rc<Vec<Ref<Visual>>> =
297 this.visual_children_snapshot().unwrap_or_else(|| Rc::new(Vec::new()));
298
299 let mut sorted_children: Option<Vec<(Ref<Visual>, usize)>> = None;
300 if this.has_non_uniform_z_index_children() && visual_children.len() > 1 {
301 let mut sorted: Vec<(Ref<Visual>, usize)> =
302 visual_children.iter().cloned().enumerate().map(|(index, visual)| (visual, index)).collect();
303 sorted.sort_by(|lhs, rhs| lhs.0.z_index().cmp(&rhs.0.z_index()).then(lhs.1.cmp(&rhs.1)));
305 sorted_children = Some(sorted);
306 }
307
308 let mut child_visual = this.child_composition_visual();
309
310 if child_visual.as_ref().is_some_and(|child| !Rc::ptr_eq(child.compositor(), composition_visual.compositor())) {
312 child_visual = None;
313 }
314
315 let mut expected_count = visual_children.len();
316 if child_visual.is_some() {
317 expected_count += 1;
318 }
319
320 let same = |a: &Rc<CompositionVisual>, b: Option<CompositionDrawListVisual>| {
321 b.is_some_and(|b| Rc::ptr_eq(a, &b))
322 };
323
324 if composition_children.count() == expected_count {
325 let mut mismatch = false;
326 for c in 0..visual_children.len() {
327 let expected = match &sorted_children {
328 Some(sorted) => sorted[c].0.composition_visual(),
329 None => visual_children[c].composition_visual(),
330 };
331 if !same(&composition_children.get(c), expected) {
332 mismatch = true;
333 break;
334 }
335 }
336
337 if let Some(child_visual) = &child_visual {
338 if !Rc::ptr_eq(&composition_children.get(composition_children.count() - 1), child_visual) {
339 mismatch = true;
340 }
341 }
342
343 if !mismatch {
344 return;
345 }
346 }
347
348 composition_children.clear();
349 match &sorted_children {
350 Some(sorted) => {
351 for (child, _) in sorted {
352 if let Some(composition_child) = child.composition_visual() {
353 composition_children.add((*composition_child).clone());
354 }
355 }
356 }
357 None => {
358 for child in visual_children.iter() {
359 if let Some(composition_child) = child.composition_visual() {
360 composition_children.add((*composition_child).clone());
361 }
362 }
363 }
364 }
365
366 if let Some(child_visual) = child_visual {
367 composition_children.add(child_visual);
368 }
369 }
370
371 fn synchronize_composition_properties(this: &Self) {
372 let Some(comp) = this.composition_visual() else { return };
373 let bounds = this.bounds();
374
375 comp.set_offset(crate::Vector3D::new(bounds.x, bounds.y, 0.0));
377 comp.set_size(crate::Vector::new(bounds.width, bounds.height));
378 comp.set_visible(this.is_visible());
379 comp.set_opacity(this.opacity() as f32);
380 comp.set_clip_to_bounds(this.clip_to_bounds());
381 comp.set_clip(this.clip().and_then(|clip| clip.platform_impl()));
382
383 let opacity_mask = this.opacity_mask();
384 let same_mask = match (comp.opacity_mask(), &opacity_mask) {
385 (Some(a), Some(b)) => *a == **b,
386 (None, None) => true,
387 _ => false,
388 };
389 if !same_mask {
390 comp.set_opacity_mask(opacity_mask);
391 }
392
393 let cache_mode = this.cache_mode().map(|cache_mode| cache_mode.get_for_compositor(comp.compositor()));
394 let same_cache_mode = match (comp.cache_mode(), &cache_mode) {
395 (Some(a), Some(b)) => Rc::ptr_eq(&a, b),
396 (None, None) => true,
397 _ => false,
398 };
399 if !same_cache_mode {
400 comp.set_cache_mode(cache_mode);
401 }
402
403 let effect = this.effect();
404 if !crate::media::EffectExtensions::effect_equals(comp.effect().as_deref(), effect.as_deref()) {
405 comp.set_effect(effect.as_ref().map(crate::media::EffectExtensions::to_immutable));
406 }
407
408 comp.set_render_options(this.render_options());
409 comp.set_text_options(this.text_options());
410
411 let mut render_transform = crate::Matrix::IDENTITY;
412
413 if this.has_mirror_transform() {
414 render_transform = crate::Matrix::new(-1.0, 0.0, 0.0, 1.0, bounds.width, 0.0);
415 }
416
417 if let Some(transform) = this.render_transform() {
418 let origin = this.render_transform_origin().to_pixels(crate::Size::new(bounds.width, bounds.height));
419 let offset = crate::Matrix::create_translation(origin.x, origin.y);
420 render_transform = render_transform * ((-offset) * transform.value() * offset);
421 }
422
423 comp.set_transform_matrix(render_transform);
424 }
425
426 fn invalidate_mirror_transform(this: &Self) {
427 let flow_direction = this.flow_direction();
428 let mut parent_flow_direction = FlowDirection::LeftToRight;
429 let bypass = this.bypass_flow_direction_policies();
430 let mut parent_bypass = false;
431
432 if let Some(parent) = this.visual_parent() {
433 parent_flow_direction = parent.flow_direction();
434 parent_bypass = parent.bypass_flow_direction_policies();
435 }
436
437 let this_should_be_mirrored = flow_direction == FlowDirection::RightToLeft && !bypass;
438 let parent_should_be_mirrored = parent_flow_direction == FlowDirection::RightToLeft && !parent_bypass;
439
440 this.set_has_mirror_transform(this_should_be_mirrored != parent_should_be_mirrored);
441 }
442}
443
444impl Drop for Visual {
453 fn drop(&mut self) {
454 for (_, subscription) in self.affects_render_subscriptions.get_mut().drain(..) {
455 subscription.dispose();
456 }
457 }
458}
459
460crate::ferro_properties! { impl Visual {
461 ferro_property!(
462 pub fn bounds_property() -> DirectProperty<Visual, Rect> {
464 FerroProperty::register_direct::<Visual, _>("Bounds", |o| o.bounds(), None, Rect::default())
465 }
466 );
467
468 ferro_property!(
469 pub fn clip_to_bounds_property() -> StyledProperty<bool> {
471 FerroProperty::register::<Visual, _>("ClipToBounds", false)
472 }
473 );
474
475 ferro_property!(
476 pub fn clip_property() -> StyledProperty<Option<Ref<Geometry>>> {
478 FerroProperty::register::<Visual, _>("Clip", None)
479 }
480 );
481
482 ferro_property!(
483 pub fn cache_mode_property() -> StyledProperty<Option<Ref<CacheMode>>> {
485 FerroProperty::register::<Visual, _>("CacheMode", None)
486 }
487 );
488
489 ferro_property!(
490 pub fn effect_property() -> StyledProperty<Option<Rc<dyn IEffect>>> {
492 FerroProperty::register::<Visual, _>("Effect", None)
493 }
494 );
495
496 ferro_property!(
497 pub fn opacity_mask_property() -> StyledProperty<Option<Rc<dyn IBrush>>> {
499 FerroProperty::register::<Visual, _>("OpacityMask", None)
500 }
501 );
502
503 ferro_property!(
504 pub fn render_transform_property() -> StyledProperty<Option<Rc<dyn ITransform>>> {
506 FerroProperty::register::<Visual, _>("RenderTransform", None)
507 }
508 );
509
510 ferro_property!(
511 pub fn render_transform_origin_property() -> StyledProperty<RelativePoint> {
513 FerroProperty::register::<Visual, _>("RenderTransformOrigin", RelativePoint::CENTER)
514 }
515 );
516
517 ferro_property!(
518 pub fn is_visible_property() -> StyledProperty<bool> {
520 FerroProperty::register::<Visual, _>("IsVisible", true)
521 }
522 );
523
524 ferro_property!(
525 pub fn opacity_property() -> StyledProperty<f64> {
527 FerroProperty::register::<Visual, _>("Opacity", 1.0)
528 }
529 );
530
531 ferro_property!(
532 pub fn has_mirror_transform_property() -> DirectProperty<Visual, bool> {
534 FerroProperty::register_direct::<Visual, _>("HasMirrorTransform", |o| o.has_mirror_transform(), None, false)
535 }
536 );
537
538 ferro_property!(
539 pub fn flow_direction_property() -> AttachedProperty<FlowDirection> {
541 FerroProperty::register_attached_with::<Visual, Visual, _>(
542 "FlowDirection",
543 StyledPropertyOptions::new(FlowDirection::LeftToRight).inherits(true),
544 )
545 }
546 );
547
548 ferro_property!(
549 pub fn visual_parent_property() -> DirectProperty<Visual, Option<Ref<Visual>>> {
551 FerroProperty::register_direct::<Visual, _>("VisualParent", |o| o.visual_parent(), None, None)
552 }
553 );
554
555 ferro_property!(
556 pub fn z_index_property() -> StyledProperty<i32> {
558 FerroProperty::register::<Visual, _>("ZIndex", 0)
559 }
560 );
561} }
562
563impl Visual {
564 fn static_constructor() {
565 Self::affects_render::<Visual>(&[
566 Self::bounds_property().as_property(),
567 Self::clip_property().as_property(),
568 Self::clip_to_bounds_property().as_property(),
569 Self::is_visible_property().as_property(),
570 Self::opacity_property().as_property(),
571 Self::opacity_mask_property().as_property(),
572 Self::effect_property().as_property(),
573 Self::has_mirror_transform_property().as_property(),
574 ]);
575 Self::render_transform_property().changed().subscribe(Self::render_transform_changed);
576 Self::z_index_property().changed().subscribe(|e| {
577 let Some(sender) = e.sender().downcast_ref::<Visual>() else { return };
578 let parent = sender.visual_parent();
579 if let Some(parent) = &parent {
580 if sender.z_index() != 0 {
581 parent.has_non_uniform_z_index_children.set(true);
582 }
583 }
584 sender.invalidate_visual();
585 if let Some(parent) = &parent {
586 if let Some(source) = parent.presentation_source() {
587 source.renderer().recalculate_children(parent);
588 }
589 }
590 });
591 }
592
593 pub fn construct() -> Self {
595 Self {
596 base: StyledElement::construct(),
597 bounds: Cell::new(Rect::default()),
598 presentation_source: RefCell::new(None),
599 visual_parent: RefCell::new(None),
600 has_mirror_transform: Cell::new(false),
601 is_effectively_visible: Cell::new(true),
602 has_non_uniform_z_index_children: Cell::new(false),
603 visual_children: OnceCell::new(),
604 attached_to_visual_tree: HandlerList::new(),
605 detached_from_visual_tree: HandlerList::new(),
606 is_effectively_visible_changed: HandlerList::new(),
607 render_options: Cell::new(RenderOptions::default()),
608 text_options: Cell::new(TextOptions::default()),
609 composition_visual: RefCell::new(None),
610 child_composition_visual: RefCell::new(None),
611 render_transform_subscription: RefCell::new(None),
612 affects_render_subscriptions: RefCell::new(Vec::new()),
613 }
614 }
615
616 pub fn new() -> Ref<Self> {
617 instantiate(Self::construct())
618 }
619
620 pub fn attached_to_visual_tree(
624 &self,
625 handler: impl Fn(&VisualTreeAttachmentEventArgs) + 'static,
626 ) -> Rc<dyn IDisposable> {
627 let token = self.attached_to_visual_tree.add(Rc::new(handler));
628 let weak = self.to_ref().downgrade();
629 Disposable::create(move || {
630 if let Some(this) = weak.upgrade() {
631 this.attached_to_visual_tree.remove(token);
632 }
633 })
634 }
635
636 pub fn detached_from_visual_tree(
638 &self,
639 handler: impl Fn(&VisualTreeAttachmentEventArgs) + 'static,
640 ) -> Rc<dyn IDisposable> {
641 let token = self.detached_from_visual_tree.add(Rc::new(handler));
642 let weak = self.to_ref().downgrade();
643 Disposable::create(move || {
644 if let Some(this) = weak.upgrade() {
645 this.detached_from_visual_tree.remove(token);
646 }
647 })
648 }
649
650 pub fn is_effectively_visible_changed(&self, handler: impl Fn() + 'static) -> Rc<dyn IDisposable> {
652 let token = self.is_effectively_visible_changed.add(Rc::new(handler));
653 let weak = self.to_ref().downgrade();
654 Disposable::create(move || {
655 if let Some(this) = weak.upgrade() {
656 this.is_effectively_visible_changed.remove(token);
657 }
658 })
659 }
660
661 #[inline]
665 pub fn bounds(&self) -> Rect {
666 self.bounds.get()
667 }
668
669 pub fn set_bounds(&self, value: Rect) {
671 self.set_and_raise_cell(Self::bounds_property(), &self.bounds, value);
672 }
673
674 pub fn clip_to_bounds(&self) -> bool {
676 self.get_value(Self::clip_to_bounds_property())
677 }
678
679 pub fn set_clip_to_bounds(&self, value: bool) {
680 self.set_value(Self::clip_to_bounds_property(), value)
681 }
682
683 pub fn clip(&self) -> Option<Ref<Geometry>> {
685 self.get_value(Self::clip_property())
686 }
687
688 pub fn set_clip(&self, value: impl Into<Nullable<Geometry>>) {
689 self.set_value(Self::clip_property(), value.into().0)
690 }
691
692 pub fn cache_mode(&self) -> Option<Ref<CacheMode>> {
694 self.get_value(Self::cache_mode_property())
695 }
696
697 pub fn set_cache_mode(&self, value: impl Into<Nullable<CacheMode>>) {
698 self.set_value(Self::cache_mode_property(), value.into().0)
699 }
700
701 pub fn effect(&self) -> Option<Rc<dyn IEffect>> {
703 self.get_value(Self::effect_property())
704 }
705
706 pub fn set_effect(&self, value: Option<Rc<dyn IEffect>>) {
707 self.set_value(Self::effect_property(), value)
708 }
709
710 pub fn opacity_mask(&self) -> Option<Rc<dyn IBrush>> {
712 self.get_value(Self::opacity_mask_property())
713 }
714
715 pub fn set_opacity_mask(&self, value: Option<Rc<dyn IBrush>>) {
716 self.set_value(Self::opacity_mask_property(), value)
717 }
718
719 pub fn render_transform(&self) -> Option<Rc<dyn ITransform>> {
721 self.get_value(Self::render_transform_property())
722 }
723
724 pub fn set_render_transform(&self, value: Option<Rc<dyn ITransform>>) {
725 self.set_value(Self::render_transform_property(), value)
726 }
727
728 pub fn render_transform_origin(&self) -> RelativePoint {
730 self.get_value(Self::render_transform_origin_property())
731 }
732
733 pub fn set_render_transform_origin(&self, value: RelativePoint) {
734 self.set_value(Self::render_transform_origin_property(), value)
735 }
736
737 #[inline]
739 pub fn is_effectively_visible(&self) -> bool {
740 self.is_effectively_visible.get()
741 }
742
743 pub fn is_visible(&self) -> bool {
745 self.get_value(Self::is_visible_property())
746 }
747
748 pub fn set_is_visible(&self, value: bool) {
749 self.set_value(Self::is_visible_property(), value)
750 }
751
752 pub fn opacity(&self) -> f64 {
754 self.get_value(Self::opacity_property())
755 }
756
757 pub fn set_opacity(&self, value: f64) {
758 self.set_value(Self::opacity_property(), value)
759 }
760
761 #[inline]
764 pub fn has_mirror_transform(&self) -> bool {
765 self.has_mirror_transform.get()
766 }
767
768 pub fn set_has_mirror_transform(&self, value: bool) {
770 self.set_and_raise_cell(Self::has_mirror_transform_property(), &self.has_mirror_transform, value);
771 }
772
773 pub fn flow_direction(&self) -> FlowDirection {
775 self.get_value(Self::flow_direction_property())
776 }
777
778 pub fn set_flow_direction(&self, value: FlowDirection) {
779 self.set_value(Self::flow_direction_property(), value)
780 }
781
782 pub fn get_flow_direction(visual: &Visual) -> FlowDirection {
784 visual.get_value(Self::flow_direction_property())
785 }
786
787 pub fn set_flow_direction_on(visual: &Visual, value: FlowDirection) {
789 visual.set_value(Self::flow_direction_property(), value)
790 }
791
792 pub fn z_index(&self) -> i32 {
798 self.get_value(Self::z_index_property())
799 }
800
801 pub fn set_z_index(&self, value: i32) {
802 self.set_value(Self::z_index_property(), value)
803 }
804
805 pub fn visual_children(&self) -> &FerroList<Ref<Visual>> {
807 self.visual_children.get_or_init(|| {
808 let list = FerroList::new();
809 list.set_reset_behavior(ResetBehavior::Remove);
810 let weak = self.to_ref().downgrade();
811 let validator_owner = weak.clone();
812 list.set_validate(Some(Rc::new(move |item: &Ref<Visual>| {
813 if let Some(parent) = item.visual_parent() {
814 let owner = validator_owner.upgrade();
815 panic!(
816 "The control {:?} already has a visual parent {:?} while trying to add it as a child of {:?}.",
817 item, parent, owner
818 );
819 }
820 })));
821 list.add_collection_changed(Rc::new(move |e: &NotifyCollectionChangedEventArgs<'_, Ref<Visual>>| {
822 if let Some(this) = weak.upgrade() {
823 this.visual_children_changed(e);
824 }
825 }));
826 list
827 })
828 }
829
830 #[inline]
832 pub fn visual_children_count(&self) -> usize {
833 self.visual_children.get().map_or(0, FerroList::count)
834 }
835
836 #[inline]
838 pub fn visual_children_snapshot(&self) -> Option<Rc<Vec<Ref<Visual>>>> {
839 self.visual_children.get().map(FerroList::snapshot)
840 }
841
842 #[inline]
844 pub fn presentation_source(&self) -> Option<Rc<dyn IPresentationSource>> {
845 self.presentation_source.borrow().clone()
846 }
847
848 pub fn visual_root(&self) -> Option<Ref<Visual>> {
850 self.presentation_source().and_then(|s| s.root_visual())
851 }
852
853 #[inline]
856 pub(crate) fn render_options(&self) -> RenderOptions {
857 self.render_options.get()
858 }
859
860 pub(crate) fn set_render_options(&self, value: RenderOptions) {
861 self.render_options.set(value);
862 self.invalidate_visual();
863 }
864
865 #[inline]
868 pub(crate) fn text_options(&self) -> TextOptions {
869 self.text_options.get()
870 }
871
872 pub(crate) fn set_text_options(&self, value: TextOptions) {
873 self.text_options.set(value);
874 self.invalidate_visual();
875 }
876
877 pub fn composition_visual(&self) -> Option<CompositionDrawListVisual> {
880 self.composition_visual.borrow().clone()
881 }
882
883 pub fn child_composition_visual(&self) -> Option<Rc<CompositionVisual>> {
886 self.child_composition_visual.borrow().clone()
887 }
888
889 pub fn set_child_composition_visual(&self, value: Option<Rc<CompositionVisual>>) {
890 *self.child_composition_visual.borrow_mut() = value;
891 }
892
893 pub fn attach_to_compositor(&self, compositor: &Rc<Compositor>) -> CompositionDrawListVisual {
896 let current = self.composition_visual();
897 match current {
898 Some(current) if Rc::ptr_eq(current.compositor(), compositor) => current,
899 _ => {
900 let created = self.create_composition_visual(compositor);
901 *self.composition_visual.borrow_mut() = Some(created.clone());
902 created
903 }
904 }
905 }
906
907 pub fn has_non_uniform_z_index_children(&self) -> bool {
909 self.has_non_uniform_z_index_children.get()
910 }
911
912 #[inline]
914 pub fn is_attached_to_visual_tree(&self) -> bool {
915 self.presentation_source.borrow().is_some()
916 }
917
918 #[inline]
920 pub fn visual_parent(&self) -> Option<Ref<Visual>> {
921 self.visual_parent.borrow().as_ref().and_then(WeakRef::upgrade)
922 }
923
924 pub(crate) fn rooted_visual_children_count() -> i32 {
927 ROOTED_VISUAL_CHILDREN_COUNT.with(Cell::get)
928 }
929
930 pub fn invalidate_visual(&self) {
934 if let Some(source) = self.presentation_source() {
935 source.renderer().add_dirty(self);
936 }
937 }
938
939 pub fn affects_render<T: ObjectType + Upcast<Visual>>(properties: &[&'static FerroProperty]) {
946 for property in properties {
947 if can_value_affect_render(property.property_type()) {
948 property.changed().subscribe(|e| {
949 if let Some(sender) = e.sender().downcast_ref::<T>() {
950 let visual: &Visual = sender.upcast();
951 visual.update_affects_render_subscription(e);
952 visual.invalidate_visual();
953 }
954 });
955 } else {
956 property.changed().subscribe(|e| {
957 if let Some(sender) = e.sender().downcast_ref::<T>() {
958 let visual: &Visual = sender.upcast();
959 visual.invalidate_visual();
960 }
961 });
962 }
963 }
964 }
965
966 fn update_affects_render_subscription(&self, e: &FerroPropertyChangedEventArgs<'_>) {
969 let property = e.property();
970 let old = {
971 let mut subscriptions = self.affects_render_subscriptions.borrow_mut();
972 let position = subscriptions.iter().position(|(p, _)| *p == property);
973 position.map(|position| subscriptions.swap_remove(position))
974 };
975 if let Some((_, subscription)) = old {
976 subscription.dispose();
977 }
978
979 let weak = self.to_ref().downgrade();
980 let subscription = subscribe_invalidated(
981 e.new_value(),
982 Rc::new(move || {
983 if let Some(target) = weak.upgrade() {
984 target.invalidate_visual();
985 }
986 }),
987 );
988 if let Some(subscription) = subscription {
989 self.affects_render_subscriptions.borrow_mut().push((property, subscription));
990 }
991 }
992
993 fn render_transform_changed(e: &FerroPropertyChangedEventArgs<'_>) {
997 let Some(sender) = e.sender().downcast_ref::<Visual>() else { return };
998
999 if sender.visual_root().is_some() {
1000 let (_, new_value) = e.get_old_and_new_value::<Option<Rc<dyn ITransform>>>();
1001
1002 let subscription = new_value.as_ref().and_then(|new_value| {
1004 let new_transform = new_value.as_any().downcast_ref::<Transform>()?;
1005 let weak = sender.to_ref().downgrade();
1006 Some(new_transform.changed(move || {
1007 if let Some(visual) = weak.upgrade() {
1008 visual.render_transform_value_changed();
1009 }
1010 }))
1011 });
1012 sender.set_render_transform_subscription(subscription);
1013
1014 sender.invalidate_visual();
1015 }
1016 }
1017
1018 fn render_transform_value_changed(&self) {
1020 self.invalidate_visual();
1021 }
1022
1023 fn set_render_transform_subscription(&self, value: Option<Rc<dyn IDisposable>>) {
1024 let previous = self.render_transform_subscription.replace(value);
1025 if let Some(previous) = previous {
1026 previous.dispose();
1027 }
1028 }
1029
1030 pub fn set_presentation_source_for_root_visual(&self, presentation_source: Option<Rc<dyn IPresentationSource>>) {
1032 let current = self.presentation_source();
1033 let same = match (¤t, &presentation_source) {
1034 (Some(a), Some(b)) => Rc::ptr_eq(a, b),
1035 (None, None) => true,
1036 _ => false,
1037 };
1038 if same {
1039 return;
1040 }
1041
1042 if let Some(current) = current {
1043 assert!(
1044 presentation_source.is_none(),
1045 "Visual is already attached to a presentation source. Only one presentation source can be \
1046 attached to a visual tree."
1047 );
1048 self.on_detached_from_visual_tree_core(&VisualTreeAttachmentEventArgs::new(None, current));
1049 }
1050
1051 *self.presentation_source.borrow_mut() = presentation_source.clone();
1052
1053 if let Some(source) = presentation_source {
1054 let e = VisualTreeAttachmentEventArgs::new(None, source);
1055 self.on_attached_to_visual_tree_core(&e);
1056 }
1057 }
1058
1059 fn update_is_effectively_visible(&self, parent_state: bool) {
1060 let is_effectively_visible = parent_state && self.is_visible();
1061 if self.is_effectively_visible.get() == is_effectively_visible {
1062 return;
1063 }
1064 self.is_effectively_visible.set(is_effectively_visible);
1065 if !self.is_effectively_visible_changed.is_empty() {
1066 for (_, handler) in self.is_effectively_visible_changed.snapshot().iter() {
1067 handler();
1068 }
1069 }
1070 if let Some(children) = self.visual_children.get() {
1072 for child in children.snapshot().iter() {
1073 child.update_is_effectively_visible(is_effectively_visible);
1074 }
1075 }
1076 }
1077
1078 fn set_visual_parent(&self, value: Option<&Ref<Visual>>) {
1079 let old = self.visual_parent();
1080 if old.as_ref() == value {
1081 return;
1082 }
1083 *self.visual_parent.borrow_mut() = value.map(Ref::downgrade);
1084
1085 if let (Some(source), Some(old)) = (self.presentation_source(), &old) {
1086 let e = VisualTreeAttachmentEventArgs::new(Some(old.clone()), source);
1087 self.on_detached_from_visual_tree_core(&e);
1088 }
1089
1090 if let Some(parent) = value {
1091 if let Some(source) = parent.presentation_source() {
1092 let e = VisualTreeAttachmentEventArgs::new(Some(parent.clone()), source);
1093 self.on_attached_to_visual_tree_core(&e);
1094 }
1095 }
1096
1097 self.on_visual_parent_changed(old.as_ref(), value);
1098 }
1099
1100 fn visual_children_changed(&self, e: &NotifyCollectionChangedEventArgs<'_, Ref<Visual>>) {
1101 match e.action {
1102 NotifyCollectionChangedAction::Add => {
1103 let this = self.to_ref();
1104 for visual in e.new_items {
1105 visual.set_visual_parent(Some(&this));
1106 }
1107 }
1108 NotifyCollectionChangedAction::Remove => {
1109 for visual in e.old_items {
1110 visual.set_visual_parent(None);
1111 }
1112 }
1113 NotifyCollectionChangedAction::Replace => {
1114 for visual in e.old_items {
1115 visual.set_visual_parent(None);
1116 }
1117 let this = self.to_ref();
1118 for visual in e.new_items {
1119 visual.set_visual_parent(Some(&this));
1120 }
1121 }
1122 NotifyCollectionChangedAction::Move | NotifyCollectionChangedAction::Reset => {}
1123 }
1124 }
1125}