ferroui_controls/
experimental_acrylic_border.rs1use crate::{Border, ControlImpl, Decorator, TopLevel, WindowTransparencyLevel};
2use ferroui_base::input::InputElementImpl;
3use ferroui_base::interactivity::InteractiveImpl;
4use ferroui_base::layout::{LayoutHelper, Layoutable, LayoutableImpl};
5use ferroui_base::media::{ExperimentalAcrylicMaterial, ImmutableExperimentalAcrylicMaterial};
6use ferroui_base::reactive::{IDisposable, ObservableExt};
7use ferroui_base::rendering::composition::{
8 CompositionDrawListVisual, CompositionExperimentalAcrylicVisual, Compositor,
9};
10use ferroui_base::{FerroObject, FerroObjectExtensions, FerroObjectImplExt, FerroPropertyChangedEventArgs};
11use std::cell::RefCell;
12use std::rc::Rc;
13use ferroui_base::{
14 ferro_class, ferro_impl_classes, ferro_property, instantiate, CornerRadius, FerroObjectImpl,
15 FerroProperty, Nullable, Ref, Size, StyledElementImpl, StyledProperty, Visual, VisualImpl, VisualImplExt,
16 VisualTreeAttachmentEventArgs,
17};
18
19#[repr(C)]
27pub struct ExperimentalAcrylicBorder {
28 base: Decorator,
29 subscription: RefCell<Option<Rc<dyn IDisposable>>>,
30 material_subscription: RefCell<Option<Rc<dyn IDisposable>>>,
31}
32
33ferro_class!(ExperimentalAcrylicBorder: Decorator);
34ferroui_base::ferro_class_info!(ExperimentalAcrylicBorder { new: ExperimentalAcrylicBorder::new });
35ferro_impl_classes!(ExperimentalAcrylicBorder: StyledElementImpl, InteractiveImpl, InputElementImpl, ControlImpl);
36
37impl FerroObjectImpl for ExperimentalAcrylicBorder {
38 fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
39 if change.property() == Self::material_property().as_property() {
40 this.update_material_subscription();
41 }
42 if change.property() == Self::corner_radius_property().as_property() {
43 this.sync_material(this.composition_visual());
44 }
45 Self::parent_on_property_changed(this, change);
46 }
47}
48
49impl VisualImpl for ExperimentalAcrylicBorder {
50 fn on_attached_to_visual_tree(this: &Self, e: &VisualTreeAttachmentEventArgs) {
51 Self::parent_on_attached_to_visual_tree(this, e);
52
53 let tl = TopLevel::get_top_level(Some(this));
54 if let Some(tl) = tl {
55 let weak_tl = tl.downgrade();
58 let weak_this = this.to_ref().downgrade();
59 let object: &FerroObject = &tl;
60 let subscription =
61 FerroObjectExtensions::get_observable(object, TopLevel::actual_transparency_level_property())
62 .subscribe_fn(move |x| {
63 let (Some(tl), Some(this)) = (weak_tl.upgrade(), weak_this.upgrade()) else {
64 return;
65 };
66 let (Some(platform_impl), Some(material)) = (tl.platform_impl(), this.material()) else {
67 return;
68 };
69 if x == WindowTransparencyLevel::transparent() || x == WindowTransparencyLevel::none() {
70 material.set_platform_transparency_compensation_level(
71 platform_impl.acrylic_compensation_levels().transparent_level(),
72 );
73 } else if x == WindowTransparencyLevel::blur() {
74 material.set_platform_transparency_compensation_level(
75 platform_impl.acrylic_compensation_levels().blur_level(),
76 );
77 } else if x == WindowTransparencyLevel::acrylic_blur() {
78 material.set_platform_transparency_compensation_level(
79 platform_impl.acrylic_compensation_levels().acrylic_blur_level(),
80 );
81 }
82 });
83 *this.subscription.borrow_mut() = Some(subscription);
84 } else if let Some(material) = this.material() {
85 material.set_platform_transparency_compensation_level(1.0);
86 }
87
88 this.update_material_subscription();
89 }
90
91 fn on_detached_from_visual_tree(this: &Self, e: &VisualTreeAttachmentEventArgs) {
92 Self::parent_on_detached_from_visual_tree(this, e);
93 this.update_material_subscription();
94 let subscription = this.subscription.borrow().clone();
95 if let Some(subscription) = subscription {
96 subscription.dispose();
97 }
98 }
99
100 fn create_composition_visual(this: &Self, compositor: &Rc<Compositor>) -> CompositionDrawListVisual {
101 let v = CompositionExperimentalAcrylicVisual::new(compositor, this);
102 this.sync_material(Some(v.as_draw_list_visual().clone()));
103
104 v.as_draw_list_visual().clone()
105 }
106}
107
108impl LayoutableImpl for ExperimentalAcrylicBorder {
109 fn measure_override(this: &Self, available_size: Size) -> Size {
111 let child = this.child();
112 LayoutHelper::measure_child(child.as_deref().map(|c| -> &Layoutable { c }), available_size, this.padding())
113 }
114
115 fn arrange_override(this: &Self, final_size: Size) -> Size {
117 let child = this.child();
118 LayoutHelper::arrange_child(child.as_deref().map(|c| -> &Layoutable { c }), final_size, this.padding())
119 }
120}
121
122ferroui_base::ferro_properties! { impl ExperimentalAcrylicBorder {
123 ferro_property!(
124 pub fn corner_radius_property() -> StyledProperty<CornerRadius> {
126 Border::corner_radius_property().add_owner::<ExperimentalAcrylicBorder>()
127 }
128 );
129
130 ferro_property!(
131 pub fn material_property() -> StyledProperty<Option<Ref<ExperimentalAcrylicMaterial>>> {
133 FerroProperty::register::<ExperimentalAcrylicBorder, _>("Material", None)
134 }
135 );
136} }
137
138impl ExperimentalAcrylicBorder {
139 fn static_constructor() {
140 Visual::affects_render::<ExperimentalAcrylicBorder>(&[
141 Self::material_property().as_property(),
142 Self::corner_radius_property().as_property(),
143 ]);
144 }
145
146 pub fn construct() -> Self {
148 Self {
149 base: Decorator::construct(),
150 subscription: RefCell::new(None),
151 material_subscription: RefCell::new(None),
152 }
153 }
154
155 fn update_material_subscription(&self) {
156 let material_subscription = self.material_subscription.borrow_mut().take();
157 if let Some(material_subscription) = material_subscription {
158 material_subscription.dispose();
159 }
160 let Some(composition_visual) = self.composition_visual() else {
161 return;
162 };
163 let Some(material) = self.material() else {
164 return;
165 };
166 let weak = self.to_ref().downgrade();
167 let material_subscription = material.property_changed(move |_| {
168 if let Some(this) = weak.upgrade() {
169 this.update_material_subscription();
170 }
171 });
172 *self.material_subscription.borrow_mut() = Some(material_subscription);
173 self.sync_material(Some(composition_visual));
174 }
175
176 fn sync_material(&self, visual: Option<CompositionDrawListVisual>) {
177 if let Some(v) = visual.and_then(|visual| CompositionExperimentalAcrylicVisual::from_visual(&visual)) {
178 v.set_corner_radius(self.corner_radius());
179 let material = self
180 .material()
181 .and_then(|material| {
182 material.to_immutable().as_any().downcast_ref::<ImmutableExperimentalAcrylicMaterial>().copied()
183 })
184 .unwrap_or_default();
185 v.set_material(material);
186 }
187 }
188
189 pub fn new() -> Ref<Self> {
190 instantiate(Self::construct())
191 }
192
193 pub fn corner_radius(&self) -> CornerRadius {
195 self.get_value(Self::corner_radius_property())
196 }
197
198 pub fn set_corner_radius(&self, value: CornerRadius) {
200 self.set_value(Self::corner_radius_property(), value)
201 }
202
203 pub fn material(&self) -> Option<Ref<ExperimentalAcrylicMaterial>> {
205 self.get_value(Self::material_property())
206 }
207
208 pub fn set_material(&self, value: impl Into<Nullable<ExperimentalAcrylicMaterial>>) {
210 self.set_value(Self::material_property(), value.into().0)
211 }
212}