i_slint_compiler/passes/
lower_component_container.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//! This pass lowers `ComponentContainer`s.
5//!
6//! The `ComponentContainer` is lowered to something like this:
7//!
8//! ```slint
9//!     ComponentContainer {
10//!         if false: Emtpy {}
11//!     }
12//! ```
13//!
14//! The Repeater that is inserted is where the ComponentContainer will embed
15//! its `ItemTree` under. It is never evaluated as a conditional, so we should
16//! be able to use some invalid expression, but that triggers asserts in later
17//! compiler passes.
18//!
19//! The Repeater that is marked as `is_component_placeholder`, so that
20//! we can generate the expected code later.
21//!
22//! The `Empty` behind the conditional `Repeater` is never used.
23
24use crate::diagnostics::BuildDiagnostics;
25use crate::langtype::ElementType;
26use crate::typeloader::TypeLoader;
27use crate::{expression_tree, object_tree::*};
28use std::cell::RefCell;
29use std::rc::Rc;
30
31pub fn lower_component_container(
32    doc: &Document,
33    type_loader: &mut TypeLoader,
34    diag: &mut BuildDiagnostics,
35) {
36    let empty_type = type_loader.global_type_registry.borrow().empty_type();
37
38    doc.visit_all_used_components(|component| {
39        recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
40            if matches!(&elem.borrow().builtin_type(), Some(b) if b.name == "ComponentContainer") {
41                diagnose_component_container(elem, diag);
42                process_component_container(elem, &empty_type);
43            }
44        })
45    });
46}
47
48fn diagnose_component_container(element: &ElementRc, diag: &mut BuildDiagnostics) {
49    let elem = element.borrow();
50    if !elem.children.is_empty() {
51        diag.push_error("ComponentContainers may not have children".into(), &*element.borrow());
52    }
53    if let Some(cip) =
54        elem.enclosing_component.upgrade().unwrap().child_insertion_point.borrow().clone()
55    {
56        if Rc::ptr_eq(&cip.parent, element) {
57            diag.push_error(
58                "The @children placeholder cannot appear in a ComponentContainer".into(),
59                &*element.borrow(),
60            );
61        }
62    }
63}
64
65fn process_component_container(element: &ElementRc, empty_type: &ElementType) {
66    let suffix = element.borrow().id.clone();
67
68    let component = Rc::new_cyclic(|component_weak| {
69        let root_element = Rc::new(RefCell::new(Element {
70            id: smol_str::format_smolstr!("component_container_internal_{}", suffix),
71            base_type: empty_type.clone(),
72            enclosing_component: component_weak.clone(),
73            ..Default::default()
74        }));
75
76        Component {
77            id: smol_str::format_smolstr!("ComponentContainerInternal_{}", suffix),
78            root_element,
79            ..Default::default()
80        }
81    });
82
83    let mut elem = element.borrow_mut();
84
85    let embedded_element = Element::make_rc(Element {
86        base_type: ElementType::Component(component.clone()),
87        id: smol_str::format_smolstr!("component_container_placeholder_{}", suffix),
88        debug: elem.debug.clone(),
89        enclosing_component: elem.enclosing_component.clone(),
90        default_fill_parent: (true, true),
91        inline_depth: elem.inline_depth,
92        repeated: Some(RepeatedElementInfo {
93            model: expression_tree::Expression::BoolLiteral(false),
94            model_data_id: Default::default(),
95            index_id: Default::default(),
96            is_conditional_element: true,
97            is_listview: None,
98        }),
99        is_component_placeholder: true,
100        ..Default::default()
101    });
102    elem.children.push(embedded_element);
103}