i_slint_compiler/passes/
lower_shadows.rs1use crate::diagnostics::BuildDiagnostics;
9use crate::expression_tree::BindingExpression;
10use crate::{expression_tree::Expression, object_tree::*};
11use crate::{expression_tree::NamedReference, typeregister::TypeRegister};
12use smol_str::{format_smolstr, SmolStr};
13use std::cell::RefCell;
14use std::collections::HashMap;
15use std::rc::Rc;
16
17fn create_box_shadow_element(
20 shadow_property_bindings: HashMap<SmolStr, BindingExpression>,
21 sibling_element: &ElementRc,
22 type_register: &TypeRegister,
23 diag: &mut BuildDiagnostics,
24) -> Option<Element> {
25 if matches!(sibling_element.borrow().builtin_type(), Some(b) if b.name != "Rectangle") {
26 for (shadow_prop_name, shadow_prop_binding) in shadow_property_bindings {
27 diag.push_error(
28 format!("The {shadow_prop_name} property is only supported on Rectangle elements right now"),
29 &shadow_prop_binding,
30 );
31 }
32 return None;
33 }
34
35 let mut element = Element {
36 id: format_smolstr!("{}-shadow", sibling_element.borrow().id),
37 base_type: type_register.lookup_builtin_element("BoxShadow").unwrap(),
38 enclosing_component: sibling_element.borrow().enclosing_component.clone(),
39 bindings: shadow_property_bindings
40 .into_iter()
41 .map(|(shadow_prop_name, expr)| {
42 (shadow_prop_name.strip_prefix("drop-shadow-").unwrap().into(), expr.into())
43 })
44 .collect(),
45 ..Default::default()
46 };
47
48 let border_radius = SmolStr::new_static("border-radius");
50 if sibling_element.borrow().bindings.contains_key(&border_radius) {
51 element.bindings.insert(
52 border_radius.clone(),
53 RefCell::new(
54 Expression::PropertyReference(NamedReference::new(sibling_element, border_radius))
55 .into(),
56 ),
57 );
58 }
59
60 Some(element)
61}
62
63fn inject_shadow_element_in_repeated_element(
66 shadow_property_bindings: HashMap<SmolStr, BindingExpression>,
67 repeated_element: &ElementRc,
68 type_register: &TypeRegister,
69 diag: &mut BuildDiagnostics,
70) {
71 let element_with_shadow_property =
72 &repeated_element.borrow().base_type.as_component().root_element.clone();
73
74 let shadow_element = match create_box_shadow_element(
75 shadow_property_bindings,
76 element_with_shadow_property,
77 type_register,
78 diag,
79 ) {
80 Some(element) => element,
81 None => return,
82 };
83
84 crate::object_tree::inject_element_as_repeated_element(
85 repeated_element,
86 Element::make_rc(shadow_element),
87 );
88}
89
90fn take_shadow_property_bindings(element: &ElementRc) -> HashMap<SmolStr, BindingExpression> {
91 crate::typeregister::RESERVED_DROP_SHADOW_PROPERTIES
92 .iter()
93 .flat_map(|(shadow_property_name, _)| {
94 let shadow_property_name = SmolStr::new(shadow_property_name);
95 let mut element = element.borrow_mut();
96 element.bindings.remove(&shadow_property_name).map(|binding| {
97 element.property_declarations.remove(&shadow_property_name);
99 (shadow_property_name, binding.into_inner())
100 })
101 })
102 .collect()
103}
104
105pub fn lower_shadow_properties(
106 component: &Rc<Component>,
107 type_register: &TypeRegister,
108 diag: &mut BuildDiagnostics,
109) {
110 for (shadow_prop_name, shadow_prop_binding) in
111 take_shadow_property_bindings(&component.root_element)
112 {
113 diag.push_warning(
114 format!("The {shadow_prop_name} property cannot be used on the root element, the shadow will not be visible"),
115 &shadow_prop_binding,
116 );
117 }
118
119 recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
120 if elem.borrow().repeated.is_some() {
126 let component = elem.borrow().base_type.as_component().clone(); let drop_shadow_properties = take_shadow_property_bindings(&component.root_element);
129 if !drop_shadow_properties.is_empty() {
130 drop(component);
131 inject_shadow_element_in_repeated_element(
132 drop_shadow_properties,
133 elem,
134 type_register,
135 diag,
136 );
137 }
138 }
139
140 let old_children = {
141 let mut elem = elem.borrow_mut();
142 let new_children = Vec::with_capacity(elem.children.len());
143 std::mem::replace(&mut elem.children, new_children)
144 };
145
146 for child in old_children {
150 let drop_shadow_properties = take_shadow_property_bindings(&child);
151 if !drop_shadow_properties.is_empty() {
152 let mut shadow_elem = match create_box_shadow_element(
153 drop_shadow_properties,
154 &child,
155 type_register,
156 diag,
157 ) {
158 Some(element) => element,
159 None => {
160 elem.borrow_mut().children.push(child);
161 continue;
162 }
163 };
164
165 shadow_elem.geometry_props.clone_from(&child.borrow().geometry_props);
166 elem.borrow_mut().children.push(ElementRc::new(shadow_elem.into()));
167 }
168 elem.borrow_mut().children.push(child);
169 }
170 });
171}