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_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 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 if let Some(e) = default_value_for_extra_properties(child, property_name) {
109 bind.expression = e;
110 }
111 }
112 }
113 (property_name.into(), bind.into())
114 })
115 .collect();
116
117 let element = Element {
118 id: format_smolstr!("{}-{}", child.borrow().id, element_name),
119 base_type: type_register.lookup_element(element_name).unwrap(),
120 enclosing_component: child.borrow().enclosing_component.clone(),
121 bindings,
122 ..Default::default()
123 };
124 element.make_rc()
125}
126
127pub fn lower_transform_properties(
129 component: &Rc<Component>,
130 tr: &TypeRegister,
131 diag: &mut BuildDiagnostics,
132) {
133 let transform_origin = crate::typeregister::transform_origin_property();
134
135 lower_property_to_element(
136 component,
137 crate::typeregister::RESERVED_TRANSFORM_PROPERTIES.iter().map(|(prop_name, _)| *prop_name),
138 std::iter::once(transform_origin.0),
139 Some(&|e, prop| {
140 let prop_div_2 = |prop: &str| Expression::BinaryExpression {
141 lhs: Expression::PropertyReference(NamedReference::new(e, prop.into())).into(),
142 op: '/',
143 rhs: Expression::NumberLiteral(2., Default::default()).into(),
144 };
145
146 match prop {
147 "transform-origin" => Some(Expression::Struct {
148 ty: transform_origin.1.clone(),
149 values: [
150 (SmolStr::new_static("x"), prop_div_2("width")),
151 (SmolStr::new_static("y"), prop_div_2("height")),
152 ]
153 .into_iter()
154 .collect(),
155 }),
156 "transform-scale-x" | "transform-scale-y" => {
157 if e.borrow().is_binding_set("transform-scale", true) {
158 Some(Expression::PropertyReference(NamedReference::new(
159 e,
160 SmolStr::new_static("transform-scale"),
161 )))
162 } else {
163 Some(Expression::NumberLiteral(1., Default::default()))
164 }
165 }
166 "transform-scale" => None,
167 "transform-rotation" => Some(Expression::NumberLiteral(0., Default::default())),
168 _ => unreachable!(),
169 }
170 }),
171 &SmolStr::new_static("Transform"),
172 tr,
173 diag,
174 );
175}