Skip to main content

i_slint_compiler/passes/
inlining.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4//! Inline each object_tree::Component within the main Component
5
6#![allow(clippy::mutable_key_type)] // pass uses identity-based keys backed by interior mutability
7
8use 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                // First, make sure that the component itself is properly inlined
35                inline_components_recursively(&c, roots, inline_selection, diag);
36
37                if c.parent_element().is_some() {
38                    // We should not inline a repeated element
39                    return;
40                }
41
42                // Inline this component.
43                if match inline_selection {
44                    InlineSelection::InlineAllComponents => true,
45                    InlineSelection::InlineOnlyRequiredComponents => {
46                        component_requires_inlining(&c)
47                            || element_require_inlining(elem)
48                            // We always inline the root in case the element that instantiate this component needs full inlining,
49                            // except when the root is a repeater component, which are never inlined.
50                            || component.parent_element().is_none() && Rc::ptr_eq(elem, &component.root_element)
51                            // We always inline other roots as a component can't be both a sub component and a root
52                            || 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    // inlined_component must be the base type of this element
90    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    // states and transitions must be lowered before inlining
113    debug_assert!(inlined_component.root_element.borrow().states.is_empty());
114    debug_assert!(inlined_component.root_element.borrow().transitions.is_empty());
115
116    // Map the old element to the new
117    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                // @children was into a PopupWindow
171                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    if let Some(orig) = &inlined_component.root_element.borrow().layout_info_v_with_constraint {
296        if let Some(_new) = &mut elem_mut.layout_info_v_with_constraint {
297            todo!("Merge layout infos");
298        } else {
299            elem_mut.layout_info_v_with_constraint = Some(orig.clone());
300        }
301    }
302    if let Some(orig) = &inlined_component.root_element.borrow().layout_info_h_with_constraint {
303        if let Some(_new) = &mut elem_mut.layout_info_h_with_constraint {
304            todo!("Merge layout infos");
305        } else {
306            elem_mut.layout_info_h_with_constraint = Some(orig.clone());
307        }
308    }
309
310    core::mem::drop(elem_mut);
311
312    let fixup_init_expression = |mut init_code: Expression| {
313        // Fix up any property references from within already collected init code.
314        visit_named_references_in_expression(&mut init_code, &mut |nr| {
315            fixup_reference(nr, &mapping)
316        });
317        fixup_element_references(&mut init_code, &mapping);
318        init_code
319    };
320    let inlined_init_code = inlined_component
321        .init_code
322        .borrow()
323        .inlined_init_code
324        .values()
325        .cloned()
326        .chain(inlined_component.init_code.borrow().constructor_code.iter().cloned())
327        .map(fixup_init_expression)
328        .collect();
329
330    root_component
331        .init_code
332        .borrow_mut()
333        .inlined_init_code
334        .insert(elem.borrow().span().offset, Expression::CodeBlock(inlined_init_code));
335
336    // Now fixup all bindings and references
337    for e in mapping.values() {
338        // Must run before visit_all_named_references_in_element to break shared
339        // GridLayoutCell Rcs (otherwise NR fixup would modify the original's cells).
340        visit_element_expressions(e, |expr, _, _| fixup_element_references(expr, &mapping));
341        // Also clone grid cells in the debug layout, which
342        // visit_all_named_references_in_element also visits.
343        for d in &mut e.borrow_mut().debug {
344            if let Some(crate::layout::Layout::GridLayout(grid)) = d.layout.as_mut() {
345                grid.clone_cells();
346            }
347        }
348        visit_all_named_references_in_element(e, |nr| fixup_reference(nr, &mapping));
349    }
350    for p in root_component.popup_windows.borrow_mut().iter_mut() {
351        fixup_reference(&mut p.x, &mapping);
352        fixup_reference(&mut p.y, &mapping);
353        if let Some(is_open) = &mut p.is_open {
354            fixup_reference(is_open, &mapping);
355        }
356    }
357    for t in root_component.timers.borrow_mut().iter_mut() {
358        fixup_reference(&mut t.interval, &mapping);
359        fixup_reference(&mut t.running, &mapping);
360        fixup_reference(&mut t.triggered, &mapping);
361    }
362    // If some element were moved into PopupWindow, we need to report error if they are used outside of the popup window.
363    if !moved_into_popup.is_empty() {
364        recurse_elem_no_borrow(&root_component.root_element.clone(), &(), &mut |e, _| {
365            if !moved_into_popup.contains(&element_key(e.clone())) {
366                visit_all_named_references_in_element(e, |nr| {
367                    if moved_into_popup.contains(&element_key(nr.element())) {
368                        diag.push_error(format!("Access to property '{nr:?}' which is inlined into a PopupWindow via @children is forbidden"), &*e.borrow());
369                    }
370                });
371            }
372        });
373    }
374}
375
376// Duplicate the element elem and all its children. And fill the mapping to point from the old to the new
377fn duplicate_element_with_mapping(
378    element: &ElementRc,
379    mapping: &mut Mapping,
380    root_component: &Rc<Component>,
381    priority_delta: i32,
382) -> ElementRc {
383    let elem = element.borrow();
384    let new = Rc::new(RefCell::new(Element {
385        base_type: elem.base_type.clone(),
386        id: elem.id.clone(),
387        property_declarations: elem.property_declarations.clone(),
388        // We will do the fixup of the references in bindings later
389        bindings: elem
390            .bindings
391            .iter()
392            .map(|b| duplicate_binding(b, mapping, root_component, priority_delta))
393            .collect(),
394        change_callbacks: elem.change_callbacks.clone(),
395        property_analysis: elem.property_analysis.clone(),
396        children: elem
397            .children
398            .iter()
399            .map(|x| duplicate_element_with_mapping(x, mapping, root_component, priority_delta))
400            .collect(),
401        repeated: elem.repeated.clone(),
402        is_component_placeholder: elem.is_component_placeholder,
403        debug: elem.debug.clone(),
404        enclosing_component: Rc::downgrade(root_component),
405        states: elem.states.clone(),
406        transitions: elem
407            .transitions
408            .iter()
409            .map(|t| duplicate_transition(t, mapping, root_component, priority_delta))
410            .collect(),
411        child_of_layout: elem.child_of_layout,
412        layout_info_prop: elem.layout_info_prop.clone(),
413        layout_info_v_with_constraint: elem.layout_info_v_with_constraint.clone(),
414        layout_info_h_with_constraint: elem.layout_info_h_with_constraint.clone(),
415        default_fill_parent: elem.default_fill_parent,
416        accessibility_props: elem.accessibility_props.clone(),
417        geometry_props: elem.geometry_props.clone(),
418        named_references: Default::default(),
419        item_index: Default::default(), // Not determined yet
420        item_index_of_first_children: Default::default(),
421        is_flickable_viewport: elem.is_flickable_viewport,
422        has_popup_child: elem.has_popup_child,
423        is_tooltip: elem.is_tooltip,
424        is_legacy_syntax: elem.is_legacy_syntax,
425        inline_depth: elem.inline_depth + 1,
426        // Deep-clone grid_layout_cell to avoid sharing between original and inlined copies.
427        // This is important because children_constraints contain NamedReferences that need
428        // to be fixed up independently for each inlined copy.
429        grid_layout_cell: elem
430            .grid_layout_cell
431            .as_ref()
432            .map(|cell| Rc::new(RefCell::new(cell.borrow().clone()))),
433    }));
434    mapping.insert(element_key(element.clone()), new.clone());
435    if let ElementType::Component(c) = &mut new.borrow_mut().base_type
436        && c.parent_element().is_some()
437    {
438        debug_assert!(Rc::ptr_eq(element, &c.parent_element().unwrap()));
439        *c = duplicate_sub_component(c, &new, mapping, priority_delta);
440    };
441
442    new
443}
444
445/// Duplicate Component for repeated element or popup window that have a parent_element
446fn duplicate_sub_component(
447    component_to_duplicate: &Rc<Component>,
448    new_parent: &ElementRc,
449    mapping: &mut Mapping,
450    priority_delta: i32,
451) -> Rc<Component> {
452    debug_assert!(component_to_duplicate.parent_element().is_some());
453    let new_component = Component {
454        node: component_to_duplicate.node.clone(),
455        id: component_to_duplicate.id.clone(),
456        root_element: duplicate_element_with_mapping(
457            &component_to_duplicate.root_element,
458            mapping,
459            component_to_duplicate, // that's the wrong one, but we fixup further
460            priority_delta,
461        ),
462        parent_element: RefCell::new(Rc::downgrade(new_parent)),
463        optimized_elements: RefCell::new(
464            component_to_duplicate
465                .optimized_elements
466                .borrow()
467                .iter()
468                .map(|e| {
469                    duplicate_element_with_mapping(
470                        e,
471                        mapping,
472                        component_to_duplicate,
473                        priority_delta,
474                    )
475                })
476                .collect(),
477        ),
478        root_constraints: component_to_duplicate.root_constraints.clone(),
479        child_insertion_point: component_to_duplicate.child_insertion_point.clone(),
480        init_code: component_to_duplicate.init_code.clone(),
481        popup_windows: Default::default(),
482        timers: component_to_duplicate.timers.clone(),
483        menu_item_tree: Default::default(),
484        exported_global_names: component_to_duplicate.exported_global_names.clone(),
485        used: component_to_duplicate.used.clone(),
486        private_properties: Default::default(),
487        inherits_popup_window: core::cell::Cell::new(false),
488        from_library: core::cell::Cell::new(false),
489    };
490
491    let new_component = Rc::new(new_component);
492    let weak = Rc::downgrade(&new_component);
493    recurse_elem(&new_component.root_element, &(), &mut |e, _| {
494        e.borrow_mut().enclosing_component = weak.clone()
495    });
496    for o in new_component.optimized_elements.borrow().iter() {
497        o.borrow_mut().enclosing_component = weak.clone()
498    }
499    *new_component.popup_windows.borrow_mut() = component_to_duplicate
500        .popup_windows
501        .borrow()
502        .iter()
503        .map(|p| duplicate_popup(p, mapping, priority_delta))
504        .collect();
505    for p in new_component.popup_windows.borrow_mut().iter_mut() {
506        fixup_reference(&mut p.x, mapping);
507        fixup_reference(&mut p.y, mapping);
508        if let Some(is_open) = &mut p.is_open {
509            fixup_reference(is_open, mapping);
510        }
511    }
512    for t in new_component.timers.borrow_mut().iter_mut() {
513        fixup_reference(&mut t.interval, mapping);
514        fixup_reference(&mut t.running, mapping);
515        fixup_reference(&mut t.triggered, mapping);
516        if let Some(e) = mapping.get(&element_key(t.element.upgrade().unwrap())) {
517            t.element = Rc::downgrade(e);
518        }
519    }
520    *new_component.menu_item_tree.borrow_mut() = component_to_duplicate
521        .menu_item_tree
522        .borrow()
523        .iter()
524        .map(|it| {
525            let new_parent =
526                mapping.get(&element_key(it.parent_element().unwrap())).unwrap().clone();
527            duplicate_sub_component(it, &new_parent, mapping, priority_delta)
528        })
529        .collect();
530    new_component
531        .root_constraints
532        .borrow_mut()
533        .visit_named_references(&mut |nr| fixup_reference(nr, mapping));
534    new_component
535}
536
537fn duplicate_popup(p: &PopupWindow, mapping: &mut Mapping, priority_delta: i32) -> PopupWindow {
538    let parent = mapping
539        .get(&element_key(p.component.parent_element().expect("must have a parent")))
540        .expect("Parent must be in the mapping")
541        .clone();
542    PopupWindow {
543        x: p.x.clone(),
544        y: p.y.clone(),
545        close_policy: p.close_policy.clone(),
546        component: duplicate_sub_component(&p.component, &parent, mapping, priority_delta),
547        parent_element: mapping
548            .get(&element_key(p.parent_element.clone()))
549            .expect("Parent element must be in the mapping")
550            .clone(),
551        is_tooltip: p.is_tooltip,
552        is_open: p.is_open.clone(),
553    }
554}
555
556/// Clone and increase the priority of a binding
557/// and duplicate its animation
558fn duplicate_binding(
559    (k, b): (&SmolStr, &RefCell<BindingExpression>),
560    mapping: &mut Mapping,
561    root_component: &Rc<Component>,
562    priority_delta: i32,
563) -> (SmolStr, RefCell<BindingExpression>) {
564    let b = b.borrow();
565    let b = BindingExpression {
566        expression: b.expression.clone(),
567        span: b.span.clone(),
568        priority: b.priority.saturating_add(priority_delta),
569        animation: b
570            .animation
571            .as_ref()
572            .map(|pa| duplicate_property_animation(pa, mapping, root_component, priority_delta)),
573        analysis: b.analysis.clone(),
574        two_way_bindings: b.two_way_bindings.clone(),
575    };
576    (k.clone(), b.into())
577}
578
579fn duplicate_property_animation(
580    v: &PropertyAnimation,
581    mapping: &mut Mapping,
582    root_component: &Rc<Component>,
583    priority_delta: i32,
584) -> PropertyAnimation {
585    match v {
586        PropertyAnimation::Static(a) => PropertyAnimation::Static(duplicate_element_with_mapping(
587            a,
588            mapping,
589            root_component,
590            priority_delta,
591        )),
592        PropertyAnimation::Transition { state_ref, animations } => PropertyAnimation::Transition {
593            state_ref: state_ref.clone(),
594            animations: animations
595                .iter()
596                .map(|a| TransitionPropertyAnimation {
597                    state_id: a.state_id,
598                    direction: a.direction,
599                    animation: duplicate_element_with_mapping(
600                        &a.animation,
601                        mapping,
602                        root_component,
603                        priority_delta,
604                    ),
605                })
606                .collect(),
607        },
608    }
609}
610
611fn fixup_reference(nr: &mut NamedReference, mapping: &Mapping) {
612    if let Some(e) = mapping.get(&element_key(nr.element())) {
613        *nr = NamedReference::new(e, nr.name().clone());
614    }
615}
616
617/// Remap all the element references stored in a grid layout (the cell items and
618/// the repeated-row child templates) through the inlining `mapping`.
619fn fixup_grid_layout(layout: &mut crate::layout::GridLayout, fxe: &impl Fn(&mut ElementRc)) {
620    for e in &mut layout.elems {
621        fxe(&mut e.item.element);
622    }
623    // Break the cell Rc sharing with the original before remapping the elements
624    // stored inside the repeated-row cells.
625    layout.clone_cells();
626    for elem in &mut layout.elems {
627        let mut cell = elem.cell.borrow_mut();
628        let Some(child_items) = &mut cell.child_items else { continue };
629        for child in child_items.iter_mut() {
630            fxe(&mut child.layout_item_mut().element);
631            if let crate::layout::RowChildTemplate::Repeated { repeated_element, .. } = child {
632                fxe(repeated_element);
633            }
634        }
635    }
636}
637
638fn fixup_element_references(expr: &mut Expression, mapping: &Mapping) {
639    let fx = |element: &mut std::rc::Weak<RefCell<Element>>| {
640        if let Some(e) = element.upgrade().and_then(|e| mapping.get(&element_key(e))) {
641            *element = Rc::downgrade(e);
642        }
643    };
644    let fxe = |element: &mut ElementRc| {
645        if let Some(e) = mapping.get(&element_key(element.clone())) {
646            *element = e.clone();
647        }
648    };
649    match expr {
650        Expression::ElementReference(element) => fx(element),
651        Expression::SolveBoxLayout(layout, _) => {
652            for e in &mut layout.elems {
653                fxe(&mut e.element);
654            }
655        }
656        Expression::ComputeBoxLayoutInfo { layout, cross_axis_size, .. } => {
657            for e in &mut layout.elems {
658                fxe(&mut e.element);
659            }
660            if let Some(cas) = cross_axis_size {
661                fixup_element_references(cas, mapping);
662            }
663        }
664        Expression::SolveGridLayout { layout, .. } | Expression::OrganizeGridLayout(layout) => {
665            fixup_grid_layout(layout, &fxe);
666        }
667        Expression::ComputeGridLayoutInfo { layout, cross_axis_size, .. } => {
668            fixup_grid_layout(layout, &fxe);
669            if let Some(cas) = cross_axis_size {
670                fixup_element_references(cas, mapping);
671            }
672        }
673        Expression::SolveFlexboxLayout(layout) => {
674            for e in &mut layout.elems {
675                fxe(&mut e.item.element);
676            }
677        }
678        Expression::ComputeFlexboxLayoutInfo { layout, cross_axis_size, .. } => {
679            for e in &mut layout.elems {
680                fxe(&mut e.item.element);
681            }
682            if let Some(cas) = cross_axis_size {
683                fixup_element_references(cas, mapping);
684            }
685        }
686        Expression::RepeaterModelReference { element }
687        | Expression::RepeaterIndexReference { element } => fx(element),
688        _ => expr.visit_mut(|e| fixup_element_references(e, mapping)),
689    }
690}
691
692fn duplicate_transition(
693    t: &Transition,
694    mapping: &mut HashMap<ByAddress<ElementRc>, Rc<RefCell<Element>>>,
695    root_component: &Rc<Component>,
696    priority_delta: i32,
697) -> Transition {
698    Transition {
699        direction: t.direction,
700        state_id: t.state_id.clone(),
701        property_animations: t
702            .property_animations
703            .iter()
704            .map(|(r, loc, anim)| {
705                (
706                    r.clone(),
707                    loc.clone(),
708                    duplicate_element_with_mapping(anim, mapping, root_component, priority_delta),
709                )
710            })
711            .collect(),
712        node: t.node.clone(),
713    }
714}
715
716// Some components need to be inlined to avoid increased complexity in handling them
717// in the code generators and subsequent passes.
718fn component_requires_inlining(component: &Rc<Component>) -> bool {
719    let root_element = &component.root_element;
720    if super::flickable::is_flickable_element(root_element) {
721        return true;
722    }
723
724    for (prop, binding) in &root_element.borrow().bindings {
725        let binding = binding.borrow();
726        // The passes that dp the drop shadow or the opacity currently won't allow this property
727        // on the top level of a component. This could be changed in the future.
728        if prop.starts_with("drop-shadow-")
729            || prop.starts_with("inner-shadow-")
730            || prop == "opacity"
731            || prop == "cache-rendering-hint"
732            || prop == "visible"
733        {
734            return true;
735        }
736        if (prop == "height" || prop == "width") && binding.expression.ty() == Type::Percent {
737            // percentage size in the root element might not make sense anyway.
738            return true;
739        }
740        if binding.animation.is_some() {
741            let lookup_result = root_element.borrow().lookup_property(prop);
742            if !lookup_result.is_valid()
743                || !lookup_result.is_local_to_component
744                || !matches!(
745                    lookup_result.property_visibility,
746                    PropertyVisibility::Private | PropertyVisibility::Output
747                )
748            {
749                // If there is an animation, we currently inline so that if this property
750                // is set with a binding, it is merged
751                return true;
752            }
753        }
754    }
755
756    false
757}
758
759fn element_require_inlining(elem: &ElementRc) -> bool {
760    if !elem.borrow().children.is_empty() {
761        // the generators assume that the children list is complete, which sub-components may break
762        return true;
763    }
764
765    // Popup windows need to be inlined for root.close() to work properly.
766    if super::lower_popups::is_popup_window(elem) {
767        return true;
768    }
769
770    for (prop, binding) in &elem.borrow().bindings {
771        if prop == "clip" {
772            // otherwise the children of the clipped items won't get moved as child of the Clip element
773            return true;
774        }
775
776        if (prop == "padding"
777            || prop == "spacing"
778            || prop.starts_with("padding-")
779            || prop.starts_with("spacing-")
780            || prop == "alignment")
781            && let ElementType::Component(base) = &elem.borrow().base_type
782            && crate::layout::is_layout(&base.root_element.borrow().base_type)
783            && !base.root_element.borrow().is_binding_set(prop, false)
784        {
785            // The layout pass need to know that this property is set
786            return true;
787        }
788
789        let binding = binding.borrow();
790        if binding.animation.is_some() && matches!(binding.expression, Expression::Invalid) {
791            // If there is an animation but no binding, we must merge the binding with its animation.
792            return true;
793        }
794    }
795
796    false
797}