1use crate::diagnostics::Spanned;
9use crate::expression_tree::{BindingExpression, Expression, Unit};
10use crate::langtype::{ElementType, Type};
11use crate::layout::Orientation;
12use crate::namedreference::NamedReference;
13use crate::object_tree::*;
14use smol_str::SmolStr;
15use std::collections::BTreeMap;
16use std::rc::Rc;
17
18pub fn materialize_fake_properties(component: &Rc<Component>) {
19 let mut to_materialize = std::collections::HashMap::new();
20
21 visit_all_named_references(component, &mut |nr| {
22 let elem = nr.element();
23 let elem = elem.borrow();
24 if !to_materialize.contains_key(nr) {
25 if let Some(ty) =
26 should_materialize(&elem.property_declarations, &elem.base_type, nr.name())
27 {
28 to_materialize.insert(nr.clone(), ty);
29 }
30 }
31 });
32
33 recurse_elem_including_sub_components_no_borrow(component, &(), &mut |elem, _| {
34 for prop in elem.borrow().bindings.keys() {
35 let nr = NamedReference::new(elem, prop.clone());
36 if let std::collections::hash_map::Entry::Vacant(e) = to_materialize.entry(nr) {
37 let elem = elem.borrow();
38 if let Some(ty) =
39 should_materialize(&elem.property_declarations, &elem.base_type, prop)
40 {
41 e.insert(ty);
42 }
43 }
44 }
45 });
46
47 for (nr, ty) in to_materialize {
48 let elem = nr.element();
49
50 elem.borrow_mut().property_declarations.insert(
51 nr.name().clone(),
52 PropertyDeclaration { property_type: ty, ..PropertyDeclaration::default() },
53 );
54
55 if !must_initialize(&elem.borrow(), nr.name()) {
56 continue;
60 }
61 if let Some(init_expr) = initialize(&elem, nr.name()) {
62 let mut elem_mut = elem.borrow_mut();
63 let span = elem_mut.to_source_location();
64 match elem_mut.bindings.entry(nr.name().clone()) {
65 std::collections::btree_map::Entry::Vacant(e) => {
66 let mut binding = BindingExpression::new_with_span(init_expr, span);
67 binding.priority = i32::MAX;
68 e.insert(binding.into());
69 }
70 std::collections::btree_map::Entry::Occupied(mut e) => {
71 e.get_mut().get_mut().expression = init_expr;
72 }
73 }
74 }
75 }
76}
77
78fn must_initialize(elem: &Element, prop: &str) -> bool {
80 match elem.bindings.get(prop) {
81 None => true,
82 Some(b) => matches!(b.borrow().expression, Expression::Invalid),
83 }
84}
85
86fn should_materialize(
88 property_declarations: &BTreeMap<SmolStr, PropertyDeclaration>,
89 base_type: &ElementType,
90 prop: &str,
91) -> Option<Type> {
92 if property_declarations.contains_key(prop) {
93 return None;
94 }
95 let has_declared_property = match base_type {
96 ElementType::Component(c) => has_declared_property(&c.root_element.borrow(), prop),
97 ElementType::Builtin(b) => b.native_class.lookup_property(prop).is_some(),
98 ElementType::Native(n) => n.lookup_property(prop).is_some(),
99 ElementType::Global | ElementType::Error => false,
100 };
101
102 if !has_declared_property {
103 let ty = crate::typeregister::reserved_property(prop).property_type;
104 if ty != Type::Invalid {
105 return Some(ty);
106 } else if prop == "close-on-click" {
107 return Some(Type::Bool);
109 } else if prop == "close-policy" {
110 return Some(Type::Enumeration(
112 crate::typeregister::BUILTIN.with(|e| e.enums.PopupClosePolicy.clone()),
113 ));
114 } else {
115 let ty = base_type.lookup_property(prop).property_type.clone();
116 return (ty != Type::Invalid).then_some(ty);
117 }
118 }
119 None
120}
121
122pub fn has_declared_property(elem: &Element, prop: &str) -> bool {
125 if elem.property_declarations.contains_key(prop) {
126 return true;
127 }
128 match &elem.base_type {
129 ElementType::Component(c) => has_declared_property(&c.root_element.borrow(), prop),
130 ElementType::Builtin(b) => b.native_class.lookup_property(prop).is_some(),
131 ElementType::Native(n) => n.lookup_property(prop).is_some(),
132 ElementType::Global | ElementType::Error => false,
133 }
134}
135
136pub fn initialize(elem: &ElementRc, name: &str) -> Option<Expression> {
138 let mut base_type = elem.borrow().base_type.clone();
139 loop {
140 base_type = match base_type {
141 ElementType::Component(ref c) => c.root_element.borrow().base_type.clone(),
142 ElementType::Builtin(b) => {
143 match b.properties.get(name).and_then(|prop| prop.default_value.expr(elem)) {
144 Some(expr) => return Some(expr),
145 None => break,
146 }
147 }
148 _ => break,
149 };
150 }
151
152 if elem.borrow().builtin_type().map_or(false, |n| n.name == "Image") {
157 if elem.borrow().layout_info_prop(Orientation::Horizontal).is_none() {
158 match name {
159 "min-width" => return Some(Expression::NumberLiteral(0., Unit::Px)),
160 "max-width" => return Some(Expression::NumberLiteral(f32::MAX as _, Unit::Px)),
161 "horizontal-stretch" => return Some(Expression::NumberLiteral(0., Unit::None)),
162 _ => {}
163 }
164 }
165
166 if elem.borrow().layout_info_prop(Orientation::Vertical).is_none() {
167 match name {
168 "min-height" => return Some(Expression::NumberLiteral(0., Unit::Px)),
169 "max-height" => return Some(Expression::NumberLiteral(f32::MAX as _, Unit::Px)),
170 "vertical-stretch" => return Some(Expression::NumberLiteral(0., Unit::None)),
171 _ => {}
172 }
173 }
174 }
175
176 let expr = match name {
177 "min-height" => layout_constraint_prop(elem, "min", Orientation::Vertical),
178 "min-width" => layout_constraint_prop(elem, "min", Orientation::Horizontal),
179 "max-height" => layout_constraint_prop(elem, "max", Orientation::Vertical),
180 "max-width" => layout_constraint_prop(elem, "max", Orientation::Horizontal),
181 "horizontal-stretch" => layout_constraint_prop(elem, "stretch", Orientation::Horizontal),
182 "vertical-stretch" => layout_constraint_prop(elem, "stretch", Orientation::Vertical),
183 "preferred-height" => layout_constraint_prop(elem, "preferred", Orientation::Vertical),
184 "preferred-width" => layout_constraint_prop(elem, "preferred", Orientation::Horizontal),
185 "opacity" => Expression::NumberLiteral(1., Unit::None),
186 "visible" => Expression::BoolLiteral(true),
187 _ => return None,
188 };
189 Some(expr)
190}
191
192fn layout_constraint_prop(elem: &ElementRc, field: &str, orient: Orientation) -> Expression {
193 let expr = match elem.borrow().layout_info_prop(orient) {
194 Some(e) => Expression::PropertyReference(e.clone()),
195 None => crate::layout::implicit_layout_info_call(elem, orient),
196 };
197 Expression::StructFieldAccess { base: expr.into(), name: field.into() }
198}