1#![allow(clippy::mutable_key_type)] use crate::diagnostics::{BuildDiagnostics, Spanned};
9use crate::expression_tree::{BindingExpression, Expression, NamedReference};
10use crate::langtype::{ElementType, Type};
11use crate::object_tree::*;
12use by_address::ByAddress;
13use smol_str::SmolStr;
14use std::cell::RefCell;
15use std::collections::{HashMap, HashSet};
16use std::rc::Rc;
17
18#[derive(Copy, Clone, Eq, PartialEq)]
19pub enum InlineSelection {
20 InlineAllComponents,
21 InlineOnlyRequiredComponents,
22}
23
24pub fn inline(doc: &Document, inline_selection: InlineSelection, diag: &mut BuildDiagnostics) {
25 fn inline_components_recursively(
26 component: &Rc<Component>,
27 roots: &HashSet<ByAddress<Rc<Component>>>,
28 inline_selection: InlineSelection,
29 diag: &mut BuildDiagnostics,
30 ) {
31 recurse_elem_no_borrow(&component.root_element, &(), &mut |elem, _| {
32 let base = elem.borrow().base_type.clone();
33 if let ElementType::Component(c) = base {
34 inline_components_recursively(&c, roots, inline_selection, diag);
36
37 if c.parent_element().is_some() {
38 return;
40 }
41
42 if match inline_selection {
44 InlineSelection::InlineAllComponents => true,
45 InlineSelection::InlineOnlyRequiredComponents => {
46 component_requires_inlining(&c)
47 || element_require_inlining(elem)
48 || component.parent_element().is_none() && Rc::ptr_eq(elem, &component.root_element)
51 || roots.contains(&ByAddress(c.clone()))
53 }
54 } {
55 inline_element(elem, &c, component, diag);
56 }
57 }
58 });
59 component.popup_windows.borrow().iter().for_each(|p| {
60 inline_components_recursively(&p.component, roots, inline_selection, diag)
61 })
62 }
63 let mut roots = HashSet::new();
64 if inline_selection == InlineSelection::InlineOnlyRequiredComponents {
65 for component in doc.exported_roots().chain(doc.popup_menu_impl.iter().cloned()) {
66 roots.insert(ByAddress(component.clone()));
67 }
68 }
69 for component in doc.exported_roots().chain(doc.popup_menu_impl.iter().cloned()) {
70 inline_components_recursively(&component, &roots, inline_selection, diag);
71 let mut init_code = component.init_code.borrow_mut();
72 let inlined_init_code = core::mem::take(&mut init_code.inlined_init_code);
73 init_code.constructor_code.splice(0..0, inlined_init_code.into_values());
74 }
75}
76
77fn element_key(e: ElementRc) -> ByAddress<ElementRc> {
78 ByAddress(e)
79}
80
81type Mapping = HashMap<ByAddress<ElementRc>, ElementRc>;
82
83fn inline_element(
84 elem: &ElementRc,
85 inlined_component: &Rc<Component>,
86 root_component: &Rc<Component>,
87 diag: &mut BuildDiagnostics,
88) {
89 debug_assert_eq!(elem.borrow().base_type, ElementType::Component(inlined_component.clone()));
91 debug_assert!(
92 inlined_component.root_element.borrow().repeated.is_none(),
93 "root element of a component cannot be repeated"
94 );
95 debug_assert!(inlined_component.parent_element().is_none());
96
97 let mut elem_mut = elem.borrow_mut();
98 let priority_delta = 1 + elem_mut.inline_depth;
99 elem_mut.base_type = inlined_component.root_element.borrow().base_type.clone();
100 elem_mut.property_declarations.extend(
101 inlined_component.root_element.borrow().property_declarations.iter().map(|(name, decl)| {
102 let mut decl = decl.clone();
103 decl.expose_in_public_api = false;
104 (name.clone(), decl)
105 }),
106 );
107
108 for (p, a) in inlined_component.root_element.borrow().property_analysis.borrow().iter() {
109 elem_mut.property_analysis.borrow_mut().entry(p.clone()).or_default().merge_with_base(a);
110 }
111
112 debug_assert!(inlined_component.root_element.borrow().states.is_empty());
114 debug_assert!(inlined_component.root_element.borrow().transitions.is_empty());
115
116 let mut mapping = HashMap::new();
118 mapping.insert(element_key(inlined_component.root_element.clone()), elem.clone());
119
120 let mut new_children = Vec::with_capacity(
121 elem_mut.children.len() + inlined_component.root_element.borrow().children.len(),
122 );
123 new_children.extend(
124 inlined_component.root_element.borrow().children.iter().map(|x| {
125 duplicate_element_with_mapping(x, &mut mapping, root_component, priority_delta)
126 }),
127 );
128
129 let mut move_children_into_popup = None;
130
131 match inlined_component.child_insertion_point.borrow().as_ref() {
132 Some(inlined_cip) => {
133 let children = std::mem::take(&mut elem_mut.children);
134 let old_count = children.len();
135 if let Some(insertion_element) = mapping.get(&element_key(inlined_cip.parent.clone())) {
136 if old_count > 0 {
137 if !Rc::ptr_eq(elem, insertion_element) {
138 debug_assert!(std::rc::Weak::ptr_eq(
139 &insertion_element.borrow().enclosing_component,
140 &elem_mut.enclosing_component,
141 ));
142 insertion_element.borrow_mut().children.splice(
143 inlined_cip.insertion_index..inlined_cip.insertion_index,
144 children,
145 );
146 } else {
147 new_children.splice(
148 inlined_cip.insertion_index..inlined_cip.insertion_index,
149 children,
150 );
151 }
152 }
153 let mut cip = root_component.child_insertion_point.borrow_mut();
154 if let Some(cip) = cip.as_mut() {
155 if Rc::ptr_eq(&cip.parent, elem) {
156 *cip = ChildrenInsertionPoint {
157 parent: insertion_element.clone(),
158 insertion_index: inlined_cip.insertion_index + cip.insertion_index,
159 node: inlined_cip.node.clone(),
160 };
161 }
162 } else if Rc::ptr_eq(elem, &root_component.root_element) {
163 *cip = Some(ChildrenInsertionPoint {
164 parent: insertion_element.clone(),
165 insertion_index: inlined_cip.insertion_index + old_count,
166 node: inlined_cip.node.clone(),
167 });
168 };
169 } else if old_count > 0 {
170 debug_assert!(inlined_component.popup_windows.borrow().iter().any(|p| Rc::ptr_eq(
172 &p.component,
173 &inlined_cip.parent.borrow().enclosing_component.upgrade().unwrap()
174 )));
175 move_children_into_popup = Some(children);
176 };
177 }
178 _ => {
179 new_children.append(&mut elem_mut.children);
180 }
181 }
182
183 elem_mut.children = new_children;
184 elem_mut.debug.extend_from_slice(&inlined_component.root_element.borrow().debug);
185
186 if let ElementType::Component(c) = &mut elem_mut.base_type
187 && c.parent_element().is_some()
188 {
189 debug_assert!(Rc::ptr_eq(elem, &c.parent_element().unwrap()));
190 *c = duplicate_sub_component(c, elem, &mut mapping, priority_delta);
191 };
192
193 root_component.optimized_elements.borrow_mut().extend(
194 inlined_component.optimized_elements.borrow().iter().map(|x| {
195 duplicate_element_with_mapping(x, &mut mapping, root_component, priority_delta)
196 }),
197 );
198 root_component.popup_windows.borrow_mut().extend(
199 inlined_component
200 .popup_windows
201 .borrow()
202 .iter()
203 .map(|p| duplicate_popup(p, &mut mapping, priority_delta)),
204 );
205
206 root_component.menu_item_tree.borrow_mut().extend(
207 inlined_component
208 .menu_item_tree
209 .borrow()
210 .iter()
211 .map(|it| duplicate_sub_component(it, elem, &mut mapping, priority_delta)),
212 );
213
214 root_component.timers.borrow_mut().extend(inlined_component.timers.borrow().iter().map(|t| {
215 let inlined_element = mapping.get(&element_key(t.element.upgrade().unwrap())).unwrap();
216
217 Timer { element: Rc::downgrade(inlined_element), ..t.clone() }
218 }));
219
220 let mut moved_into_popup = HashSet::new();
221 if let Some(children) = move_children_into_popup {
222 let child_insertion_point = inlined_component.child_insertion_point.borrow();
223 let inlined_cip = child_insertion_point.as_ref().unwrap();
224
225 let insertion_element = mapping.get(&element_key(inlined_cip.parent.clone())).unwrap();
226 debug_assert!(!std::rc::Weak::ptr_eq(
227 &insertion_element.borrow().enclosing_component,
228 &elem_mut.enclosing_component,
229 ));
230 debug_assert!(root_component.popup_windows.borrow().iter().any(|p| Rc::ptr_eq(
231 &p.component,
232 &insertion_element.borrow().enclosing_component.upgrade().unwrap()
233 )));
234 for c in &children {
235 recurse_elem(c, &(), &mut |e, _| {
236 e.borrow_mut().enclosing_component =
237 insertion_element.borrow().enclosing_component.clone();
238 moved_into_popup.insert(element_key(e.clone()));
239 });
240 }
241 insertion_element
242 .borrow_mut()
243 .children
244 .splice(inlined_cip.insertion_index..inlined_cip.insertion_index, children);
245 let mut cip = root_component.child_insertion_point.borrow_mut();
246 if let Some(cip) = cip.as_mut() {
247 if Rc::ptr_eq(&cip.parent, elem) {
248 *cip = ChildrenInsertionPoint {
249 parent: insertion_element.clone(),
250 insertion_index: inlined_cip.insertion_index + cip.insertion_index,
251 node: inlined_cip.node.clone(),
252 };
253 }
254 } else {
255 *cip = Some(ChildrenInsertionPoint {
256 parent: insertion_element.clone(),
257 insertion_index: inlined_cip.insertion_index,
258 node: inlined_cip.node.clone(),
259 });
260 };
261 }
262
263 for (k, val) in inlined_component.root_element.borrow().bindings.iter() {
264 match elem_mut.bindings.entry(k.clone()) {
265 std::collections::btree_map::Entry::Vacant(entry) => {
266 let priority = &mut entry.insert(val.clone()).get_mut().priority;
267 *priority = priority.saturating_add(priority_delta);
268 }
269 std::collections::btree_map::Entry::Occupied(mut entry) => {
270 let entry = entry.get_mut().get_mut();
271 if entry.merge_with(&val.borrow()) {
272 entry.priority = entry.priority.saturating_add(priority_delta);
273 }
274 }
275 }
276 }
277 for (k, val) in inlined_component.root_element.borrow().change_callbacks.iter() {
278 match elem_mut.change_callbacks.entry(k.clone()) {
279 std::collections::btree_map::Entry::Vacant(entry) => {
280 entry.insert(val.clone());
281 }
282 std::collections::btree_map::Entry::Occupied(mut entry) => {
283 entry.get_mut().get_mut().splice(0..0, val.borrow().iter().cloned());
284 }
285 }
286 }
287
288 if let Some(orig) = &inlined_component.root_element.borrow().layout_info_prop {
289 if let Some(_new) = &mut elem_mut.layout_info_prop {
290 todo!("Merge layout infos");
291 } else {
292 elem_mut.layout_info_prop = Some(orig.clone());
293 }
294 }
295
296 core::mem::drop(elem_mut);
297
298 let fixup_init_expression = |mut init_code: Expression| {
299 visit_named_references_in_expression(&mut init_code, &mut |nr| {
301 fixup_reference(nr, &mapping)
302 });
303 fixup_element_references(&mut init_code, &mapping);
304 init_code
305 };
306 let inlined_init_code = inlined_component
307 .init_code
308 .borrow()
309 .inlined_init_code
310 .values()
311 .cloned()
312 .chain(inlined_component.init_code.borrow().constructor_code.iter().cloned())
313 .map(fixup_init_expression)
314 .collect();
315
316 root_component
317 .init_code
318 .borrow_mut()
319 .inlined_init_code
320 .insert(elem.borrow().span().offset, Expression::CodeBlock(inlined_init_code));
321
322 for e in mapping.values() {
324 visit_element_expressions(e, |expr, _, _| fixup_element_references(expr, &mapping));
327 for d in &mut e.borrow_mut().debug {
330 if let Some(crate::layout::Layout::GridLayout(grid)) = d.layout.as_mut() {
331 grid.clone_cells();
332 }
333 }
334 visit_all_named_references_in_element(e, |nr| fixup_reference(nr, &mapping));
335 }
336 for p in root_component.popup_windows.borrow_mut().iter_mut() {
337 fixup_reference(&mut p.x, &mapping);
338 fixup_reference(&mut p.y, &mapping);
339 }
340 for t in root_component.timers.borrow_mut().iter_mut() {
341 fixup_reference(&mut t.interval, &mapping);
342 fixup_reference(&mut t.running, &mapping);
343 fixup_reference(&mut t.triggered, &mapping);
344 }
345 if !moved_into_popup.is_empty() {
347 recurse_elem_no_borrow(&root_component.root_element.clone(), &(), &mut |e, _| {
348 if !moved_into_popup.contains(&element_key(e.clone())) {
349 visit_all_named_references_in_element(e, |nr| {
350 if moved_into_popup.contains(&element_key(nr.element())) {
351 diag.push_error(format!("Access to property '{nr:?}' which is inlined into a PopupWindow via @children is forbidden"), &*e.borrow());
352 }
353 });
354 }
355 });
356 }
357}
358
359fn duplicate_element_with_mapping(
361 element: &ElementRc,
362 mapping: &mut Mapping,
363 root_component: &Rc<Component>,
364 priority_delta: i32,
365) -> ElementRc {
366 let elem = element.borrow();
367 let new = Rc::new(RefCell::new(Element {
368 base_type: elem.base_type.clone(),
369 id: elem.id.clone(),
370 property_declarations: elem.property_declarations.clone(),
371 bindings: elem
373 .bindings
374 .iter()
375 .map(|b| duplicate_binding(b, mapping, root_component, priority_delta))
376 .collect(),
377 change_callbacks: elem.change_callbacks.clone(),
378 property_analysis: elem.property_analysis.clone(),
379 children: elem
380 .children
381 .iter()
382 .map(|x| duplicate_element_with_mapping(x, mapping, root_component, priority_delta))
383 .collect(),
384 repeated: elem.repeated.clone(),
385 is_component_placeholder: elem.is_component_placeholder,
386 debug: elem.debug.clone(),
387 enclosing_component: Rc::downgrade(root_component),
388 states: elem.states.clone(),
389 transitions: elem
390 .transitions
391 .iter()
392 .map(|t| duplicate_transition(t, mapping, root_component, priority_delta))
393 .collect(),
394 child_of_layout: elem.child_of_layout,
395 layout_info_prop: elem.layout_info_prop.clone(),
396 default_fill_parent: elem.default_fill_parent,
397 accessibility_props: elem.accessibility_props.clone(),
398 geometry_props: elem.geometry_props.clone(),
399 named_references: Default::default(),
400 item_index: Default::default(), item_index_of_first_children: Default::default(),
402 is_flickable_viewport: elem.is_flickable_viewport,
403 has_popup_child: elem.has_popup_child,
404 is_legacy_syntax: elem.is_legacy_syntax,
405 inline_depth: elem.inline_depth + 1,
406 grid_layout_cell: elem
410 .grid_layout_cell
411 .as_ref()
412 .map(|cell| Rc::new(RefCell::new(cell.borrow().clone()))),
413 }));
414 mapping.insert(element_key(element.clone()), new.clone());
415 if let ElementType::Component(c) = &mut new.borrow_mut().base_type
416 && c.parent_element().is_some()
417 {
418 debug_assert!(Rc::ptr_eq(element, &c.parent_element().unwrap()));
419 *c = duplicate_sub_component(c, &new, mapping, priority_delta);
420 };
421
422 new
423}
424
425fn duplicate_sub_component(
427 component_to_duplicate: &Rc<Component>,
428 new_parent: &ElementRc,
429 mapping: &mut Mapping,
430 priority_delta: i32,
431) -> Rc<Component> {
432 debug_assert!(component_to_duplicate.parent_element().is_some());
433 let new_component = Component {
434 node: component_to_duplicate.node.clone(),
435 id: component_to_duplicate.id.clone(),
436 root_element: duplicate_element_with_mapping(
437 &component_to_duplicate.root_element,
438 mapping,
439 component_to_duplicate, priority_delta,
441 ),
442 parent_element: RefCell::new(Rc::downgrade(new_parent)),
443 optimized_elements: RefCell::new(
444 component_to_duplicate
445 .optimized_elements
446 .borrow()
447 .iter()
448 .map(|e| {
449 duplicate_element_with_mapping(
450 e,
451 mapping,
452 component_to_duplicate,
453 priority_delta,
454 )
455 })
456 .collect(),
457 ),
458 root_constraints: component_to_duplicate.root_constraints.clone(),
459 child_insertion_point: component_to_duplicate.child_insertion_point.clone(),
460 init_code: component_to_duplicate.init_code.clone(),
461 popup_windows: Default::default(),
462 timers: component_to_duplicate.timers.clone(),
463 menu_item_tree: Default::default(),
464 exported_global_names: component_to_duplicate.exported_global_names.clone(),
465 used: component_to_duplicate.used.clone(),
466 private_properties: Default::default(),
467 inherits_popup_window: core::cell::Cell::new(false),
468 from_library: core::cell::Cell::new(false),
469 };
470
471 let new_component = Rc::new(new_component);
472 let weak = Rc::downgrade(&new_component);
473 recurse_elem(&new_component.root_element, &(), &mut |e, _| {
474 e.borrow_mut().enclosing_component = weak.clone()
475 });
476 for o in new_component.optimized_elements.borrow().iter() {
477 o.borrow_mut().enclosing_component = weak.clone()
478 }
479 *new_component.popup_windows.borrow_mut() = component_to_duplicate
480 .popup_windows
481 .borrow()
482 .iter()
483 .map(|p| duplicate_popup(p, mapping, priority_delta))
484 .collect();
485 for p in new_component.popup_windows.borrow_mut().iter_mut() {
486 fixup_reference(&mut p.x, mapping);
487 fixup_reference(&mut p.y, mapping);
488 }
489 for t in new_component.timers.borrow_mut().iter_mut() {
490 fixup_reference(&mut t.interval, mapping);
491 fixup_reference(&mut t.running, mapping);
492 fixup_reference(&mut t.triggered, mapping);
493 }
494 *new_component.menu_item_tree.borrow_mut() = component_to_duplicate
495 .menu_item_tree
496 .borrow()
497 .iter()
498 .map(|it| {
499 let new_parent =
500 mapping.get(&element_key(it.parent_element().unwrap())).unwrap().clone();
501 duplicate_sub_component(it, &new_parent, mapping, priority_delta)
502 })
503 .collect();
504 new_component
505 .root_constraints
506 .borrow_mut()
507 .visit_named_references(&mut |nr| fixup_reference(nr, mapping));
508 new_component
509}
510
511fn duplicate_popup(p: &PopupWindow, mapping: &mut Mapping, priority_delta: i32) -> PopupWindow {
512 let parent = mapping
513 .get(&element_key(p.component.parent_element().expect("must have a parent")))
514 .expect("Parent must be in the mapping")
515 .clone();
516 PopupWindow {
517 x: p.x.clone(),
518 y: p.y.clone(),
519 close_policy: p.close_policy.clone(),
520 component: duplicate_sub_component(&p.component, &parent, mapping, priority_delta),
521 parent_element: mapping
522 .get(&element_key(p.parent_element.clone()))
523 .expect("Parent element must be in the mapping")
524 .clone(),
525 }
526}
527
528fn duplicate_binding(
531 (k, b): (&SmolStr, &RefCell<BindingExpression>),
532 mapping: &mut Mapping,
533 root_component: &Rc<Component>,
534 priority_delta: i32,
535) -> (SmolStr, RefCell<BindingExpression>) {
536 let b = b.borrow();
537 let b = BindingExpression {
538 expression: b.expression.clone(),
539 span: b.span.clone(),
540 priority: b.priority.saturating_add(priority_delta),
541 animation: b
542 .animation
543 .as_ref()
544 .map(|pa| duplicate_property_animation(pa, mapping, root_component, priority_delta)),
545 analysis: b.analysis.clone(),
546 two_way_bindings: b.two_way_bindings.clone(),
547 };
548 (k.clone(), b.into())
549}
550
551fn duplicate_property_animation(
552 v: &PropertyAnimation,
553 mapping: &mut Mapping,
554 root_component: &Rc<Component>,
555 priority_delta: i32,
556) -> PropertyAnimation {
557 match v {
558 PropertyAnimation::Static(a) => PropertyAnimation::Static(duplicate_element_with_mapping(
559 a,
560 mapping,
561 root_component,
562 priority_delta,
563 )),
564 PropertyAnimation::Transition { state_ref, animations } => PropertyAnimation::Transition {
565 state_ref: state_ref.clone(),
566 animations: animations
567 .iter()
568 .map(|a| TransitionPropertyAnimation {
569 state_id: a.state_id,
570 direction: a.direction,
571 animation: duplicate_element_with_mapping(
572 &a.animation,
573 mapping,
574 root_component,
575 priority_delta,
576 ),
577 })
578 .collect(),
579 },
580 }
581}
582
583fn fixup_reference(nr: &mut NamedReference, mapping: &Mapping) {
584 if let Some(e) = mapping.get(&element_key(nr.element())) {
585 *nr = NamedReference::new(e, nr.name().clone());
586 }
587}
588
589fn fixup_element_references(expr: &mut Expression, mapping: &Mapping) {
590 let fx = |element: &mut std::rc::Weak<RefCell<Element>>| {
591 if let Some(e) = element.upgrade().and_then(|e| mapping.get(&element_key(e))) {
592 *element = Rc::downgrade(e);
593 }
594 };
595 let fxe = |element: &mut ElementRc| {
596 if let Some(e) = mapping.get(&element_key(element.clone())) {
597 *element = e.clone();
598 }
599 };
600 match expr {
601 Expression::ElementReference(element) => fx(element),
602 Expression::SolveBoxLayout(l, _) | Expression::ComputeBoxLayoutInfo(l, _) => {
603 for e in &mut l.elems {
604 fxe(&mut e.element);
605 }
606 }
607 Expression::SolveGridLayout { layout, .. }
608 | Expression::OrganizeGridLayout(layout)
609 | Expression::ComputeGridLayoutInfo { layout, .. } => {
610 for e in &mut layout.elems {
611 fxe(&mut e.item.element);
612 }
613 layout.clone_cells();
614 }
615 Expression::SolveFlexboxLayout(layout)
616 | Expression::ComputeFlexboxLayoutInfo(layout, _) => {
617 for e in &mut layout.elems {
618 fxe(&mut e.item.element);
619 }
620 }
621 Expression::RepeaterModelReference { element }
622 | Expression::RepeaterIndexReference { element } => fx(element),
623 _ => expr.visit_mut(|e| fixup_element_references(e, mapping)),
624 }
625}
626
627fn duplicate_transition(
628 t: &Transition,
629 mapping: &mut HashMap<ByAddress<ElementRc>, Rc<RefCell<Element>>>,
630 root_component: &Rc<Component>,
631 priority_delta: i32,
632) -> Transition {
633 Transition {
634 direction: t.direction,
635 state_id: t.state_id.clone(),
636 property_animations: t
637 .property_animations
638 .iter()
639 .map(|(r, loc, anim)| {
640 (
641 r.clone(),
642 loc.clone(),
643 duplicate_element_with_mapping(anim, mapping, root_component, priority_delta),
644 )
645 })
646 .collect(),
647 node: t.node.clone(),
648 }
649}
650
651fn component_requires_inlining(component: &Rc<Component>) -> bool {
654 let root_element = &component.root_element;
655 if super::flickable::is_flickable_element(root_element) {
656 return true;
657 }
658
659 for (prop, binding) in &root_element.borrow().bindings {
660 let binding = binding.borrow();
661 if prop.starts_with("drop-shadow-")
664 || prop == "opacity"
665 || prop == "cache-rendering-hint"
666 || prop == "visible"
667 {
668 return true;
669 }
670 if (prop == "height" || prop == "width") && binding.expression.ty() == Type::Percent {
671 return true;
673 }
674 if binding.animation.is_some() {
675 let lookup_result = root_element.borrow().lookup_property(prop);
676 if !lookup_result.is_valid()
677 || !lookup_result.is_local_to_component
678 || !matches!(
679 lookup_result.property_visibility,
680 PropertyVisibility::Private | PropertyVisibility::Output
681 )
682 {
683 return true;
686 }
687 }
688 }
689
690 false
691}
692
693fn element_require_inlining(elem: &ElementRc) -> bool {
694 if !elem.borrow().children.is_empty() {
695 return true;
697 }
698
699 if super::lower_popups::is_popup_window(elem) {
701 return true;
702 }
703
704 for (prop, binding) in &elem.borrow().bindings {
705 if prop == "clip" {
706 return true;
708 }
709
710 if (prop == "padding"
711 || prop == "spacing"
712 || prop.starts_with("padding-")
713 || prop.starts_with("spacing-")
714 || prop == "alignment")
715 && let ElementType::Component(base) = &elem.borrow().base_type
716 && crate::layout::is_layout(&base.root_element.borrow().base_type)
717 && !base.root_element.borrow().is_binding_set(prop, false)
718 {
719 return true;
721 }
722
723 let binding = binding.borrow();
724 if binding.animation.is_some() && matches!(binding.expression, Expression::Invalid) {
725 return true;
727 }
728 }
729
730 false
731}