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::{SmolStr, ToSmolStr, format_smolstr};
13use std::rc::Rc;
14
15pub(crate) fn lower_property_to_element(
20 component: &Rc<Component>,
21 property_names: impl Iterator<Item = &'static str> + Clone,
22 extra_properties: impl Iterator<Item = &'static str> + Clone,
23 default_value_for_extra_properties: Option<&dyn Fn(&ElementRc, &str) -> Option<Expression>>,
24 element_name: &SmolStr,
25 type_register: &TypeRegister,
26 diag: &mut BuildDiagnostics,
27) {
28 for property_name in property_names.clone() {
29 if let Some(b) = component.root_element.borrow().bindings.get(property_name) {
30 diag.push_warning(
31 format!(
32 "The {property_name} property cannot be used on the root element, it will not be applied"
33 ),
34 &*b.borrow(),
35 );
36 }
37 }
38
39 object_tree::recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
40 if elem.borrow().base_type.to_smolstr() == *element_name {
41 return;
42 }
43
44 let old_children = {
45 let mut elem = elem.borrow_mut();
46 let new_children = Vec::with_capacity(elem.children.len());
47 std::mem::replace(&mut elem.children, new_children)
48 };
49
50 let has_property_binding = |e: &ElementRc| {
51 property_names.clone().any(|property_name| {
52 e.borrow().base_type.lookup_property(property_name).property_type != Type::Invalid
53 && (e.borrow().bindings.contains_key(property_name)
54 || e.borrow()
55 .property_analysis
56 .borrow()
57 .get(property_name)
58 .is_some_and(|a| a.is_set || a.is_linked))
59 })
60 };
61
62 for mut child in old_children {
63 if child.borrow().repeated.is_some() {
64 let root_elem = child.borrow().base_type.as_component().root_element.clone();
65 if has_property_binding(&root_elem) {
66 object_tree::inject_element_as_repeated_element(
67 &child,
68 create_property_element(
69 &root_elem,
70 property_names.clone().chain(extra_properties.clone()),
71 default_value_for_extra_properties,
72 element_name,
73 type_register,
74 ),
75 )
76 }
77 } else if has_property_binding(&child) {
78 let new_child = create_property_element(
79 &child,
80 property_names.clone().chain(extra_properties.clone()),
81 default_value_for_extra_properties,
82 element_name,
83 type_register,
84 );
85 crate::object_tree::adjust_geometry_for_injected_parent(&new_child, &child);
86 new_child.borrow_mut().children.push(child);
87 child = new_child;
88 }
89
90 elem.borrow_mut().children.push(child);
91 }
92 });
93}
94
95fn create_property_element(
96 child: &ElementRc,
97 properties: impl Iterator<Item = &'static str>,
98 default_value_for_extra_properties: Option<&dyn Fn(&ElementRc, &str) -> Option<Expression>>,
99 element_name: &SmolStr,
100 type_register: &TypeRegister,
101) -> ElementRc {
102 let bindings = properties
103 .map(|property_name| {
104 let property_name = SmolStr::new_static(property_name);
105 let mut bind = BindingExpression::new_two_way(
106 NamedReference::new(child, property_name.clone()).into(),
107 );
108 if let Some(default_value_for_extra_properties) = default_value_for_extra_properties
109 && !child.borrow().bindings.contains_key(&property_name)
110 && let Some(e) = default_value_for_extra_properties(child, &property_name)
111 {
112 bind.expression = e;
113 }
114 (property_name, bind.into())
115 })
116 .collect();
117
118 let element = Element {
119 id: format_smolstr!("{}-{}", child.borrow().id, element_name),
120 base_type: type_register.lookup_element(element_name).unwrap(),
121 enclosing_component: child.borrow().enclosing_component.clone(),
122 bindings,
123 ..Default::default()
124 };
125 element.make_rc()
126}
127
128pub fn lower_transform_properties(
130 component: &Rc<Component>,
131 tr: &TypeRegister,
132 diag: &mut BuildDiagnostics,
133) {
134 let transform_origin = crate::typeregister::transform_origin_property();
135
136 lower_property_to_element(
137 component,
138 crate::typeregister::RESERVED_TRANSFORM_PROPERTIES.iter().map(|(prop_name, _)| *prop_name),
139 std::iter::once(transform_origin.0),
140 Some(&|e, prop| {
141 let prop_div_2 = |prop: &str| Expression::BinaryExpression {
142 lhs: Expression::PropertyReference(NamedReference::new(e, prop.into())).into(),
143 op: '/',
144 rhs: Expression::NumberLiteral(2., Default::default()).into(),
145 };
146
147 match prop {
148 "transform-origin" => Some(Expression::Struct {
149 ty: transform_origin.1.clone(),
150 values: [
151 (SmolStr::new_static("x"), prop_div_2("width")),
152 (SmolStr::new_static("y"), prop_div_2("height")),
153 ]
154 .into_iter()
155 .collect(),
156 }),
157 "transform-scale-x" | "transform-scale-y" => {
158 if e.borrow().is_binding_set("transform-scale", true) {
159 Some(Expression::PropertyReference(NamedReference::new(
160 e,
161 SmolStr::new_static("transform-scale"),
162 )))
163 } else {
164 Some(Expression::NumberLiteral(1., Default::default()))
165 }
166 }
167 "transform-scale" => None,
168 "transform-rotation" => Some(Expression::NumberLiteral(0., Default::default())),
169 _ => unreachable!(),
170 }
171 }),
172 &SmolStr::new_static("Transform"),
173 tr,
174 diag,
175 );
176}