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