ferroui_controls/primitives/
light_dismiss_overlay_layer.rs1use super::VisualLayerManager;
2use crate::{Border, ControlImpl, TopLevel};
3use ferroui_base::input::{InputElement, InputElementImpl};
4use ferroui_base::interactivity::InteractiveImpl;
5use ferroui_base::layout::LayoutableImpl;
6use ferroui_base::media::{Brushes, IBrush};
7use ferroui_base::reactive::IDisposable;
8use ferroui_base::rendering::ICustomHitTest;
9use ferroui_base::{
10 ferro_class, ferro_impl_classes, instantiate, FerroObjectImpl, Point, Ref, StyledElementImpl,
11 Visual, VisualImpl, WeakRef,
12};
13use std::cell::RefCell;
14use std::rc::Rc;
15
16#[repr(C)]
19pub struct LightDismissOverlayLayer {
20 base: Border,
21 registrations: RefCell<Vec<Rc<Registration>>>,
22}
23
24ferro_class!(LightDismissOverlayLayer: Border);
25ferroui_base::ferro_class_info!(LightDismissOverlayLayer { new: LightDismissOverlayLayer::new });
26ferro_impl_classes!(
27 LightDismissOverlayLayer: StyledElementImpl,
28 LayoutableImpl,
29 InteractiveImpl,
30 InputElementImpl,
31 ControlImpl
32);
33
34ferroui_base::ferro_impl_classes!(LightDismissOverlayLayer: FerroObjectImpl);
35
36impl VisualImpl for LightDismissOverlayLayer {
37 fn custom_hit_test(this: &Self, point: Point) -> Option<bool> {
38 Some(ICustomHitTest::hit_test(this, point))
39 }
40
41 fn custom_hit_test_geometry(
42 this: &Self,
43 geometry: &Ref<ferroui_base::media::Geometry>,
44 ) -> Option<ferroui_base::media::IntersectionResult> {
45 Some(ICustomHitTest::hit_test_geometry(this, geometry))
46 }
47}
48
49impl ICustomHitTest for LightDismissOverlayLayer {
50 fn hit_test(&self, point: Point) -> bool {
51 LightDismissOverlayLayer::hit_test(self, point)
52 }
53}
54
55impl LightDismissOverlayLayer {
56 fn static_constructor() {
57 let transparent: Rc<dyn IBrush> = Brushes::transparent();
58 Border::background_property().override_default_value::<LightDismissOverlayLayer>(Some(transparent));
59 Visual::is_visible_property().override_default_value::<LightDismissOverlayLayer>(false);
60 }
61
62 pub fn new() -> Ref<Self> {
63 instantiate(Self { base: Border::construct(), registrations: RefCell::new(Vec::new()) })
64 }
65
66 pub fn input_pass_through_element(&self) -> Option<Ref<InputElement>> {
69 let last = self.registrations.borrow().last().cloned();
70 last.and_then(|registration| registration.input_pass_through_element())
71 }
72
73 pub fn get_light_dismiss_overlay_layer(visual: &Visual) -> Option<Ref<LightDismissOverlayLayer>> {
76 let manager = match visual.to_ref().cast::<TopLevel>() {
77 Some(top_level) => {
78 top_level.get_template_descendants().into_iter().find_map(|v| v.cast::<VisualLayerManager>())
79 }
80 None => visual.find_ancestor_of_type::<VisualLayerManager>(false),
81 };
82
83 manager.map(|manager| manager.light_dismiss_overlay_layer())
84 }
85
86 pub fn register(&self, input_pass_through_element: Option<&Ref<InputElement>>) -> Rc<dyn IDisposable> {
90 let registration = Rc::new(Registration {
91 owner: RefCell::new(Some(self.to_ref().downgrade())),
92 input_pass_through_element: input_pass_through_element.map(Ref::downgrade),
93 });
94 self.registrations.borrow_mut().push(registration.clone());
95 self.update_state();
96 registration
97 }
98
99 pub fn hit_test(&self, point: Point) -> bool {
102 if let Some(v) = self.input_pass_through_element() {
103 if let Some(ie) = self.visual_root().and_then(|root| root.cast::<InputElement>()) {
104 let this: Ref<Visual> = self.to_ref().upcast();
105 if let Some(hit) = ie.input_hit_test_filtered(point, &|x: &Visual| !x.to_ref().ptr_eq(&this), true) {
106 return !v.is_visual_ancestor_of(&hit);
107 }
108 }
109 }
110
111 true
112 }
113
114 fn unregister(&self, registration: &Registration) {
115 self.registrations.borrow_mut().retain(|r| !std::ptr::eq(Rc::as_ptr(r), registration));
116 self.update_state();
117 }
118
119 fn update_state(&self) {
120 let is_visible = !self.registrations.borrow().is_empty();
121 self.set_is_visible(is_visible);
122 }
123}
124
125struct Registration {
126 owner: RefCell<Option<WeakRef<LightDismissOverlayLayer>>>,
127 input_pass_through_element: Option<WeakRef<InputElement>>,
128}
129
130impl Registration {
131 fn input_pass_through_element(&self) -> Option<Ref<InputElement>> {
132 self.input_pass_through_element.as_ref().and_then(WeakRef::upgrade)
133 }
134}
135
136impl IDisposable for Registration {
137 fn dispose(&self) {
138 let owner = self.owner.borrow_mut().take();
139 if let Some(owner) = owner.and_then(|owner| owner.upgrade()) {
140 owner.unregister(self);
141 }
142 }
143}