i_slint_compiler/passes/
lower_property_to_element.rs1use crate::diagnostics::BuildDiagnostics;
8use crate::expression_tree::{BindingExpression, Expression, NamedReference};
9use crate::langtype::Type;
10use crate::object_tree::{self, Component, Element, ElementRc};
11use crate::typeregister::TypeRegister;
12use smol_str::{format_smolstr, SmolStr, ToSmolStr};
13use std::rc::Rc;
14
15pub(crate) fn lower_property_to_element(
20 component: &Rc<Component>,
21 property_name: &'static str,
22 extra_properties: impl Iterator<Item = &'static str> + Clone,
23 default_value_for_extra_properties: Option<&dyn Fn(&ElementRc, &str) -> Expression>,
24 element_name: &SmolStr,
25 type_register: &TypeRegister,
26 diag: &mut BuildDiagnostics,
27) {
28 if let Some(b) = component.root_element.borrow().bindings.get(property_name) {
29 diag.push_warning(
30 format!(
31 "The {property_name} property cannot be used on the root element, it will not be applied"
32 ),
33 &*b.borrow(),
34 );
35 }
36
37 object_tree::recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
38 if elem.borrow().base_type.to_smolstr() == *element_name {
39 return;
40 }
41
42 let old_children = {
43 let mut elem = elem.borrow_mut();
44 let new_children = Vec::with_capacity(elem.children.len());
45 std::mem::replace(&mut elem.children, new_children)
46 };
47
48 let has_property_binding = |e: &ElementRc| {
49 e.borrow().base_type.lookup_property(property_name).property_type != Type::Invalid
50 && (e.borrow().bindings.contains_key(property_name)
51 || e.borrow()
52 .property_analysis
53 .borrow()
54 .get(property_name)
55 .is_some_and(|a| a.is_set || a.is_linked))
56 };
57
58 for mut child in old_children {
59 if child.borrow().repeated.is_some() {
60 let root_elem = child.borrow().base_type.as_component().root_element.clone();
61 if has_property_binding(&root_elem) {
62 object_tree::inject_element_as_repeated_element(
63 &child,
64 create_property_element(
65 &root_elem,
66 property_name,
67 extra_properties.clone(),
68 default_value_for_extra_properties,
69 element_name,
70 type_register,
71 ),
72 )
73 }
74 } else if has_property_binding(&child) {
75 let new_child = create_property_element(
76 &child,
77 property_name,
78 extra_properties.clone(),
79 default_value_for_extra_properties,
80 element_name,
81 type_register,
82 );
83 crate::object_tree::adjust_geometry_for_injected_parent(&new_child, &child);
84 new_child.borrow_mut().children.push(child);
85 child = new_child;
86 }
87
88 elem.borrow_mut().children.push(child);
89 }
90 });
91}
92
93fn create_property_element(
94 child: &ElementRc,
95 property_name: &'static str,
96 extra_properties: impl Iterator<Item = &'static str>,
97 default_value_for_extra_properties: Option<&dyn Fn(&ElementRc, &str) -> Expression>,
98 element_name: &SmolStr,
99 type_register: &TypeRegister,
100) -> ElementRc {
101 let bindings = core::iter::once(property_name)
102 .chain(extra_properties)
103 .map(|property_name| {
104 let mut bind =
105 BindingExpression::new_two_way(NamedReference::new(child, property_name.into()));
106 if let Some(default_value_for_extra_properties) = default_value_for_extra_properties {
107 if !child.borrow().bindings.contains_key(property_name) {
108 bind.expression = default_value_for_extra_properties(child, property_name)
109 }
110 }
111 (property_name.into(), bind.into())
112 })
113 .collect();
114
115 let element = Element {
116 id: format_smolstr!("{}-{}", child.borrow().id, property_name),
117 base_type: type_register.lookup_element(element_name).unwrap(),
118 enclosing_component: child.borrow().enclosing_component.clone(),
119 bindings,
120 ..Default::default()
121 };
122 element.make_rc()
123}