1use crate::diagnostics::BuildDiagnostics;
8use crate::expression_tree::{BindingExpression, Expression, NamedReference};
9use crate::langtype::{PropertyLookupMode, 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().binding(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,
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()
53 .base_type
54 .lookup_property(property_name, PropertyLookupMode::ComponentLocal)
55 .property_type
56 != Type::Invalid
57 && (e.borrow().binding(property_name).is_some()
58 || e.borrow()
59 .property_analysis
60 .borrow()
61 .get(property_name)
62 .is_some_and(|a| a.is_set || a.is_linked))
63 })
64 };
65
66 for mut child in old_children {
67 if child.borrow().repeated.is_some() {
68 let root_elem = child.borrow().base_type.as_component().root_element.clone();
69 if has_property_binding(&root_elem) {
70 object_tree::inject_element_as_repeated_element(
71 &child,
72 create_property_element(
73 &root_elem,
74 property_names.clone().chain(extra_properties.clone()),
75 default_value_for_extra_properties,
76 element_name,
77 type_register,
78 ),
79 )
80 }
81 } else if has_property_binding(&child) {
82 let new_child = create_property_element(
83 &child,
84 property_names.clone().chain(extra_properties.clone()),
85 default_value_for_extra_properties,
86 element_name,
87 type_register,
88 );
89 crate::object_tree::adjust_geometry_for_injected_parent(&new_child, &child);
90 new_child.borrow_mut().children.push(child);
91 child = new_child;
92 }
93
94 elem.borrow_mut().children.push(child);
95 }
96 });
97}
98
99fn create_property_element(
100 child: &ElementRc,
101 properties: impl Iterator<Item = &'static str>,
102 default_value_for_extra_properties: Option<&dyn Fn(&ElementRc, &str) -> Option<Expression>>,
103 element_name: &SmolStr,
104 type_register: &TypeRegister,
105) -> ElementRc {
106 let bindings = properties
107 .map(|property_name| {
108 let property_name = SmolStr::new_static(property_name);
109 let mut bind = BindingExpression::new_two_way(
110 NamedReference::new(child, property_name.clone()).into(),
111 );
112 if let Some(default_value_for_extra_properties) = default_value_for_extra_properties
113 && child.borrow().binding(&property_name).is_none()
114 && let Some(e) = default_value_for_extra_properties(child, &property_name)
115 {
116 bind.expression = e;
117 }
118 (property_name, bind.into())
119 })
120 .collect();
121
122 let element = Element {
123 id: format_smolstr!("{}-{}", child.borrow().id, element_name),
124 base_type: type_register.lookup_element(element_name).unwrap(),
125 enclosing_component: child.borrow().enclosing_component.clone(),
126 bindings,
127 is_injected_wrapper_element: true,
128 ..Default::default()
129 };
130 element.make_rc()
131}
132
133pub fn transform_property_default_value(
134 element: &ElementRc,
135 property_name: &str,
136) -> Option<Expression> {
137 let transform_origin = crate::typeregister::transform_origin_property();
138
139 let prop_div_2 = |prop: &str| Expression::BinaryExpression {
140 lhs: Expression::PropertyReference(NamedReference::new(element, prop.into())).into(),
141 op: '/',
142 rhs: Expression::NumberLiteral(2., Default::default()).into(),
143 source_location: None,
144 };
145
146 match property_name {
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 element.borrow().is_binding_set("transform-scale", true) {
158 Some(Expression::PropertyReference(NamedReference::new(
159 element,
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
172pub fn lower_transform_properties(
174 component: &Rc<Component>,
175 tr: &TypeRegister,
176 diag: &mut BuildDiagnostics,
177) {
178 let transform_origin = crate::typeregister::transform_origin_property();
179
180 lower_property_to_element(
181 component,
182 crate::typeregister::RESERVED_TRANSFORM_PROPERTIES.iter().map(|(prop_name, _)| *prop_name),
183 std::iter::once(transform_origin.0),
184 Some(&|e, prop| transform_property_default_value(e, prop)),
185 &SmolStr::new_static("Transform"),
186 tr,
187 diag,
188 );
189}