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: bindings.into(),
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.set_binding(
96 target_property,
97 Expression::PropertyReference(NamedReference::new(
98 sibling_element,
99 source_property,
100 ))
101 .into(),
102 );
103 }
104 }
105
106 Some(element)
107}
108
109fn prepend_inner_shadow_child(parent: &ElementRc, inner_elem: Element) {
110 let inner_rc = ElementRc::new(inner_elem.into());
111 inner_rc.borrow_mut().geometry_props = Some(GeometryProps::new(&inner_rc));
112 for property_name in ["width", "height"] {
113 inner_rc.borrow_mut().set_binding(
114 property_name.into(),
115 Expression::PropertyReference(NamedReference::new(
116 parent,
117 SmolStr::new_static(property_name),
118 ))
119 .into(),
120 );
121 }
122 parent.borrow_mut().children.insert(0, inner_rc);
123}
124
125fn inject_shadow_element_in_repeated_element(
128 shadow_property_bindings: HashMap<SmolStr, BindingExpression>,
129 repeated_element: &ElementRc,
130 type_register: &TypeRegister,
131 diag: &mut BuildDiagnostics,
132) {
133 let element_with_shadow_property =
134 &repeated_element.borrow().base_type.as_component().root_element.clone();
135
136 let shadow_element = match create_box_shadow_element(
137 shadow_property_bindings,
138 element_with_shadow_property,
139 ShadowKind::Drop,
140 type_register,
141 diag,
142 ) {
143 Some(element) => element,
144 None => return,
145 };
146
147 crate::object_tree::inject_element_as_repeated_element(
148 repeated_element,
149 Element::make_rc(shadow_element),
150 );
151}
152
153fn take_shadow_property_bindings(
154 element: &ElementRc,
155 kind: ShadowKind,
156) -> HashMap<SmolStr, BindingExpression> {
157 kind.property_list()
158 .iter()
159 .flat_map(|(shadow_property_name, _)| {
160 let shadow_property_name = SmolStr::new(shadow_property_name);
161 let mut element = element.borrow_mut();
162 element.take_binding(&shadow_property_name).map(|binding| {
163 element.property_declarations.remove(&shadow_property_name);
165 (shadow_property_name, binding)
166 })
167 })
168 .collect()
169}
170
171pub fn lower_shadow_properties(
172 component: &Rc<Component>,
173 type_register: &TypeRegister,
174 diag: &mut BuildDiagnostics,
175) {
176 for kind in [ShadowKind::Drop, ShadowKind::Inner] {
177 for (shadow_prop_name, shadow_prop_binding) in
178 take_shadow_property_bindings(&component.root_element, kind)
179 {
180 diag.push_warning(
181 format!("The {shadow_prop_name} property cannot be used on the root element, the shadow will not be visible"),
182 &shadow_prop_binding,
183 );
184 }
185 }
186
187 recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
188 if elem.borrow().repeated.is_some() {
192 let (drop_shadow_properties, inner_shadow_properties) = {
195 let component = elem.borrow().base_type.as_component().clone();
196 let drop = take_shadow_property_bindings(&component.root_element, ShadowKind::Drop);
197 let inner =
198 take_shadow_property_bindings(&component.root_element, ShadowKind::Inner);
199 (drop, inner)
200 };
201
202 if !drop_shadow_properties.is_empty() {
203 inject_shadow_element_in_repeated_element(
204 drop_shadow_properties,
205 elem,
206 type_register,
207 diag,
208 );
209 if !inner_shadow_properties.is_empty() {
212 let rect_child = elem
213 .borrow()
214 .base_type
215 .as_component()
216 .root_element
217 .borrow()
218 .children
219 .first()
220 .cloned();
221 if let Some(rect_child) = rect_child
222 && let Some(inner_elem) = create_box_shadow_element(
223 inner_shadow_properties,
224 &rect_child,
225 ShadowKind::Inner,
226 type_register,
227 diag,
228 )
229 {
230 prepend_inner_shadow_child(&rect_child, inner_elem);
231 }
232 }
233 } else if !inner_shadow_properties.is_empty() {
234 let root = elem.borrow().base_type.as_component().root_element.clone();
236 if let Some(inner_elem) = create_box_shadow_element(
237 inner_shadow_properties,
238 &root,
239 ShadowKind::Inner,
240 type_register,
241 diag,
242 ) {
243 prepend_inner_shadow_child(&root, inner_elem);
244 }
245 }
246 }
247
248 let old_children = {
249 let mut elem = elem.borrow_mut();
250 let new_children = Vec::with_capacity(elem.children.len());
251 std::mem::replace(&mut elem.children, new_children)
252 };
253
254 for child in old_children {
257 let drop_shadow_properties = take_shadow_property_bindings(&child, ShadowKind::Drop);
258 let inner_shadow_properties = take_shadow_property_bindings(&child, ShadowKind::Inner);
259
260 if !drop_shadow_properties.is_empty()
261 && let Some(mut shadow_elem) = create_box_shadow_element(
262 drop_shadow_properties,
263 &child,
264 ShadowKind::Drop,
265 type_register,
266 diag,
267 )
268 {
269 shadow_elem.geometry_props.clone_from(&child.borrow().geometry_props);
270 shadow_elem.z_order = child.borrow().z_order.clone();
273 elem.borrow_mut().children.push(ElementRc::new(shadow_elem.into()));
274 }
275
276 if !inner_shadow_properties.is_empty()
277 && let Some(shadow_elem) = create_box_shadow_element(
278 inner_shadow_properties,
279 &child,
280 ShadowKind::Inner,
281 type_register,
282 diag,
283 )
284 {
285 prepend_inner_shadow_child(&child, shadow_elem);
286 }
287
288 elem.borrow_mut().children.push(child);
289 }
290 });
291}