1use crate::diagnostics::BuildDiagnostics;
8use crate::expression_tree::BindingExpression;
9use crate::{expression_tree::Expression, object_tree::*};
10use crate::{expression_tree::NamedReference, typeregister::TypeRegister};
11use smol_str::{SmolStr, format_smolstr};
12use std::cell::RefCell;
13use std::collections::HashMap;
14use std::rc::Rc;
15
16#[derive(Copy, Clone)]
17enum ShadowKind {
18 Drop,
19 Inner,
20}
21
22impl ShadowKind {
23 fn prefix(self) -> &'static str {
24 match self {
25 ShadowKind::Drop => "drop-shadow-",
26 ShadowKind::Inner => "inner-shadow-",
27 }
28 }
29
30 fn property_list(self) -> &'static [(&'static str, crate::langtype::Type)] {
31 match self {
32 ShadowKind::Drop => crate::typeregister::RESERVED_DROP_SHADOW_PROPERTIES,
33 ShadowKind::Inner => crate::typeregister::RESERVED_INNER_SHADOW_PROPERTIES,
34 }
35 }
36}
37
38fn create_box_shadow_element(
40 shadow_property_bindings: HashMap<SmolStr, BindingExpression>,
41 sibling_element: &ElementRc,
42 kind: ShadowKind,
43 type_register: &TypeRegister,
44 diag: &mut BuildDiagnostics,
45) -> Option<Element> {
46 if matches!(sibling_element.borrow().builtin_type(), Some(b) if b.name != "Rectangle") {
47 for (shadow_prop_name, shadow_prop_binding) in shadow_property_bindings {
48 diag.push_error(
49 format!("The {shadow_prop_name} property is only supported on Rectangle elements right now"),
50 &shadow_prop_binding,
51 );
52 }
53 return None;
54 }
55
56 let prefix = kind.prefix();
57 let id_suffix = match kind {
58 ShadowKind::Drop => "shadow",
59 ShadowKind::Inner => "inner-shadow",
60 };
61
62 let mut bindings: crate::object_tree::BindingsMap = shadow_property_bindings
63 .into_iter()
64 .map(|(shadow_prop_name, expr)| {
65 (shadow_prop_name.strip_prefix(prefix).unwrap().into(), expr.into())
66 })
67 .collect();
68
69 if matches!(kind, ShadowKind::Inner) {
70 bindings.insert(
71 SmolStr::new_static("inset"),
72 RefCell::new(Expression::BoolLiteral(true).into()),
73 );
74 }
75
76 let mut element = Element {
77 id: format_smolstr!("{}-{}", sibling_element.borrow().id, id_suffix),
78 base_type: type_register.lookup_builtin_element("BoxShadow").unwrap(),
79 enclosing_component: sibling_element.borrow().enclosing_component.clone(),
80 bindings,
81 ..Default::default()
82 };
83
84 for property_name in super::border_radius::BORDER_RADIUS_PROPERTIES {
85 let source_property = if sibling_element.borrow().is_binding_set(property_name, true) {
86 Some(SmolStr::new_static(property_name))
87 } else if sibling_element.borrow().is_binding_set("border-radius", true) {
88 Some(SmolStr::new_static("border-radius"))
89 } else {
90 None
91 };
92
93 if let Some(source_property) = source_property {
94 let target_property = SmolStr::new_static(property_name);
95 element.bindings.insert(
96 target_property,
97 RefCell::new(
98 Expression::PropertyReference(NamedReference::new(
99 sibling_element,
100 source_property,
101 ))
102 .into(),
103 ),
104 );
105 }
106 }
107
108 Some(element)
109}
110
111fn prepend_inner_shadow_child(parent: &ElementRc, inner_elem: Element) {
112 let inner_rc = ElementRc::new(inner_elem.into());
113 inner_rc.borrow_mut().geometry_props = Some(GeometryProps::new(&inner_rc));
114 for property_name in ["width", "height"] {
115 inner_rc.borrow_mut().bindings.insert(
116 property_name.into(),
117 RefCell::new(
118 Expression::PropertyReference(NamedReference::new(
119 parent,
120 SmolStr::new_static(property_name),
121 ))
122 .into(),
123 ),
124 );
125 }
126 parent.borrow_mut().children.insert(0, inner_rc);
127}
128
129fn inject_shadow_element_in_repeated_element(
132 shadow_property_bindings: HashMap<SmolStr, BindingExpression>,
133 repeated_element: &ElementRc,
134 type_register: &TypeRegister,
135 diag: &mut BuildDiagnostics,
136) {
137 let element_with_shadow_property =
138 &repeated_element.borrow().base_type.as_component().root_element.clone();
139
140 let shadow_element = match create_box_shadow_element(
141 shadow_property_bindings,
142 element_with_shadow_property,
143 ShadowKind::Drop,
144 type_register,
145 diag,
146 ) {
147 Some(element) => element,
148 None => return,
149 };
150
151 crate::object_tree::inject_element_as_repeated_element(
152 repeated_element,
153 Element::make_rc(shadow_element),
154 );
155}
156
157fn take_shadow_property_bindings(
158 element: &ElementRc,
159 kind: ShadowKind,
160) -> HashMap<SmolStr, BindingExpression> {
161 kind.property_list()
162 .iter()
163 .flat_map(|(shadow_property_name, _)| {
164 let shadow_property_name = SmolStr::new(shadow_property_name);
165 let mut element = element.borrow_mut();
166 element.bindings.remove(&shadow_property_name).map(|binding| {
167 element.property_declarations.remove(&shadow_property_name);
169 (shadow_property_name, binding.into_inner())
170 })
171 })
172 .collect()
173}
174
175pub fn lower_shadow_properties(
176 component: &Rc<Component>,
177 type_register: &TypeRegister,
178 diag: &mut BuildDiagnostics,
179) {
180 for kind in [ShadowKind::Drop, ShadowKind::Inner] {
181 for (shadow_prop_name, shadow_prop_binding) in
182 take_shadow_property_bindings(&component.root_element, kind)
183 {
184 diag.push_warning(
185 format!("The {shadow_prop_name} property cannot be used on the root element, the shadow will not be visible"),
186 &shadow_prop_binding,
187 );
188 }
189 }
190
191 recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
192 if elem.borrow().repeated.is_some() {
196 let (drop_shadow_properties, inner_shadow_properties) = {
199 let component = elem.borrow().base_type.as_component().clone();
200 let drop = take_shadow_property_bindings(&component.root_element, ShadowKind::Drop);
201 let inner =
202 take_shadow_property_bindings(&component.root_element, ShadowKind::Inner);
203 (drop, inner)
204 };
205
206 if !drop_shadow_properties.is_empty() {
207 inject_shadow_element_in_repeated_element(
208 drop_shadow_properties,
209 elem,
210 type_register,
211 diag,
212 );
213 if !inner_shadow_properties.is_empty() {
216 let rect_child = elem
217 .borrow()
218 .base_type
219 .as_component()
220 .root_element
221 .borrow()
222 .children
223 .first()
224 .cloned();
225 if let Some(rect_child) = rect_child
226 && let Some(inner_elem) = create_box_shadow_element(
227 inner_shadow_properties,
228 &rect_child,
229 ShadowKind::Inner,
230 type_register,
231 diag,
232 )
233 {
234 prepend_inner_shadow_child(&rect_child, inner_elem);
235 }
236 }
237 } else if !inner_shadow_properties.is_empty() {
238 let root = elem.borrow().base_type.as_component().root_element.clone();
240 if let Some(inner_elem) = create_box_shadow_element(
241 inner_shadow_properties,
242 &root,
243 ShadowKind::Inner,
244 type_register,
245 diag,
246 ) {
247 prepend_inner_shadow_child(&root, inner_elem);
248 }
249 }
250 }
251
252 let old_children = {
253 let mut elem = elem.borrow_mut();
254 let new_children = Vec::with_capacity(elem.children.len());
255 std::mem::replace(&mut elem.children, new_children)
256 };
257
258 for child in old_children {
261 let drop_shadow_properties = take_shadow_property_bindings(&child, ShadowKind::Drop);
262 let inner_shadow_properties = take_shadow_property_bindings(&child, ShadowKind::Inner);
263
264 if !drop_shadow_properties.is_empty()
265 && let Some(mut shadow_elem) = create_box_shadow_element(
266 drop_shadow_properties,
267 &child,
268 ShadowKind::Drop,
269 type_register,
270 diag,
271 )
272 {
273 shadow_elem.geometry_props.clone_from(&child.borrow().geometry_props);
274 elem.borrow_mut().children.push(ElementRc::new(shadow_elem.into()));
275 }
276
277 if !inner_shadow_properties.is_empty()
278 && let Some(shadow_elem) = create_box_shadow_element(
279 inner_shadow_properties,
280 &child,
281 ShadowKind::Inner,
282 type_register,
283 diag,
284 )
285 {
286 prepend_inner_shadow_child(&child, shadow_elem);
287 }
288
289 elem.borrow_mut().children.push(child);
290 }
291 });
292}