1use by_address::ByAddress;
5
6use super::lower_expression::{ExpressionLoweringCtx, ExpressionLoweringCtxInner};
7use crate::CompilerConfiguration;
8use crate::expression_tree::Expression as tree_Expression;
9use crate::langtype::{
10 BuiltinPrivateStruct, BuiltinPublicStruct, ElementType, Struct, StructName, Type,
11};
12use crate::llr::item_tree::*;
13use crate::namedreference::NamedReference;
14use crate::object_tree::{self, Component, ElementRc, PropertyAnalysis, PropertyVisibility};
15use smol_str::{SmolStr, format_smolstr};
16use std::collections::{BTreeMap, HashMap};
17use std::rc::Rc;
18use typed_index_collections::TiVec;
19
20pub fn lower_to_item_tree(
21 document: &crate::object_tree::Document,
22 compiler_config: &CompilerConfiguration,
23) -> CompilationUnit {
24 let mut state = LoweringState::default();
25
26 #[cfg(feature = "bundle-translations")]
27 {
28 state.translation_builder = document.translation_builder.clone();
29 }
30
31 let mut globals = TiVec::new();
32 for g in &document.used_types.borrow().globals {
33 let count = globals.next_key();
34 globals.push(lower_global(g, count, &mut state));
35 }
36 for (g, l) in document.used_types.borrow().globals.iter().zip(&mut globals) {
37 lower_global_expressions(g, &mut state, l);
38 }
39
40 for c in &document.used_types.borrow().sub_components {
41 let sc = lower_sub_component(c, &mut state, None, compiler_config);
42 let idx = state.push_sub_component(sc);
43 state.sub_component_mapping.insert(ByAddress(c.clone()), idx);
44 }
45
46 let public_components = document
47 .exported_roots()
48 .map(|component| {
49 let mut sc = lower_sub_component(&component, &mut state, None, compiler_config);
50 let public_properties = public_properties(&component, &sc.mapping, &state);
51 sc.sub_component.name = component.id.clone();
52 let item_tree = ItemTree {
53 tree: make_tree(&state, &component.root_element, &sc, &[]),
54 root: state.push_sub_component(sc),
55 };
56 PublicComponent {
58 item_tree,
59 public_properties,
60 private_properties: component.private_properties.borrow().clone(),
61 name: component.id.clone(),
62 }
63 })
64 .collect();
65
66 let popup_menu = document.popup_menu_impl.as_ref().map(|c| {
67 let sc = lower_sub_component(c, &mut state, None, compiler_config);
68 let sub_menu = sc.mapping.map_property_reference(
69 &NamedReference::new(&c.root_element, SmolStr::new_static("sub-menu")),
70 &state,
71 );
72 let activated = sc.mapping.map_property_reference(
73 &NamedReference::new(&c.root_element, SmolStr::new_static("activated")),
74 &state,
75 );
76 let close = sc.mapping.map_property_reference(
77 &NamedReference::new(&c.root_element, SmolStr::new_static("close")),
78 &state,
79 );
80 let entries = sc.mapping.map_property_reference(
81 &NamedReference::new(&c.root_element, SmolStr::new_static("entries")),
82 &state,
83 );
84 let item_tree = ItemTree {
85 tree: make_tree(&state, &c.root_element, &sc, &[]),
86 root: state.push_sub_component(sc),
87 };
88 PopupMenu { item_tree, sub_menu, activated, close, entries }
89 });
90
91 let mut root = CompilationUnit {
92 public_components,
93 globals,
94 sub_components: state.sub_components.into_iter().map(|sc| sc.sub_component).collect(),
95 used_sub_components: document
96 .used_types
97 .borrow()
98 .sub_components
99 .iter()
100 .map(|tree_sub_compo| state.sub_component_mapping[&ByAddress(tree_sub_compo.clone())])
101 .collect(),
102 has_debug_info: compiler_config.debug_info,
103 popup_menu,
104 #[cfg(feature = "bundle-translations")]
105 translations: state.translation_builder.map(|x| x.result()),
106 };
107 super::optim_passes::run_passes(&mut root);
108 root
109}
110
111#[derive(Debug, Clone)]
112pub enum LoweredElement {
113 SubComponent { sub_component_index: SubComponentInstanceIdx },
114 NativeItem { item_index: ItemInstanceIdx },
115 Repeated { repeated_index: RepeatedElementIdx },
116 ComponentPlaceholder { repeated_index: u32 },
117}
118
119#[derive(Default, Debug, Clone)]
120pub struct LoweredSubComponentMapping {
121 pub element_mapping: HashMap<ByAddress<ElementRc>, LoweredElement>,
122 pub property_mapping: HashMap<NamedReference, MemberReference>,
123 pub repeater_count: u32,
124 pub container_count: u32,
125}
126
127impl LoweredSubComponentMapping {
128 pub fn map_property_reference(
129 &self,
130 from: &NamedReference,
131 state: &LoweringState,
132 ) -> MemberReference {
133 if let Some(x) = self.property_mapping.get(from) {
134 return x.clone();
135 }
136 if let Some(x) = state.global_properties.get(from) {
137 return x.clone();
138 }
139 let element = from.element();
140 if let Some(alias) = element
141 .borrow()
142 .property_declarations
143 .get(from.name())
144 .and_then(|x| x.is_alias.as_ref())
145 {
146 return self.map_property_reference(alias, state);
147 }
148 match self.element_mapping.get(&element.clone().into()).unwrap() {
149 LoweredElement::SubComponent { sub_component_index } => {
150 if let ElementType::Component(base) = &element.borrow().base_type {
151 let mut prop_ref = state.map_property_reference(&NamedReference::new(
152 &base.root_element,
153 from.name().clone(),
154 ));
155 if let MemberReference::Relative { parent_level, local_reference } =
156 &mut prop_ref
157 {
158 assert_eq!(*parent_level, 0, "the sub-component had no parents");
159 local_reference.sub_component_path.insert(0, *sub_component_index);
160 }
161 return prop_ref;
162 }
163 unreachable!()
164 }
165 LoweredElement::NativeItem { item_index } => MemberReference::Relative {
166 parent_level: 0,
167 local_reference: LocalMemberReference {
168 sub_component_path: Vec::new(),
169 reference: LocalMemberIndex::Native {
170 item_index: *item_index,
171 prop_name: from.name().clone(),
172 },
173 },
174 },
175 LoweredElement::Repeated { .. } => {
176 panic!(
177 "Trying to map property {from:?} on a repeated element {} of type {:?}",
178 element.borrow().id,
179 element.borrow().base_type
180 );
181 }
182 LoweredElement::ComponentPlaceholder { .. } => unreachable!(),
183 }
184 }
185}
186
187pub struct LoweredSubComponent {
188 sub_component: SubComponent,
189 mapping: LoweredSubComponentMapping,
190}
191
192#[derive(Default)]
193pub struct LoweringState {
194 global_properties: HashMap<NamedReference, MemberReference>,
195 sub_components: TiVec<SubComponentIdx, LoweredSubComponent>,
196 pub sub_component_mapping: HashMap<ByAddress<Rc<Component>>, SubComponentIdx>,
197 #[cfg(feature = "bundle-translations")]
198 pub translation_builder: Option<crate::translations::TranslationsBuilder>,
199}
200
201impl LoweringState {
202 pub fn map_property_reference(&self, from: &NamedReference) -> MemberReference {
203 if let Some(x) = self.global_properties.get(from) {
204 return x.clone();
205 }
206
207 let element = from.element();
208 let sc = self.sub_component(&element.borrow().enclosing_component.upgrade().unwrap());
209 sc.mapping.map_property_reference(from, self)
210 }
211
212 fn sub_component<'a>(&'a self, component: &Rc<Component>) -> &'a LoweredSubComponent {
213 &self.sub_components[self.sub_component_idx(component)]
214 }
215
216 fn sub_component_idx(&self, component: &Rc<Component>) -> SubComponentIdx {
217 self.sub_component_mapping[&ByAddress(component.clone())]
218 }
219
220 fn push_sub_component(&mut self, sc: LoweredSubComponent) -> SubComponentIdx {
221 self.sub_components.push_and_get_key(sc)
222 }
223}
224
225fn component_id(component: &Rc<Component>) -> SmolStr {
226 if component.is_global() {
227 component.root_element.borrow().id.clone()
228 } else if component.from_library.get() {
229 component.id.clone()
230 } else if component.id.is_empty() {
231 format_smolstr!("Component_{}", component.root_element.borrow().id)
232 } else {
233 format_smolstr!("{}_{}", component.id, component.root_element.borrow().id)
234 }
235}
236
237fn lower_sub_component(
238 component: &Rc<Component>,
239 state: &mut LoweringState,
240 parent_context: Option<&ExpressionLoweringCtxInner>,
241 compiler_config: &CompilerConfiguration,
242) -> LoweredSubComponent {
243 let mut sub_component = SubComponent {
244 name: component_id(component),
245 properties: Default::default(),
246 callbacks: Default::default(),
247 functions: Default::default(),
248 items: Default::default(),
249 repeated: Default::default(),
250 component_containers: Default::default(),
251 popup_windows: Default::default(),
252 menu_item_trees: Vec::new(),
253 timers: Default::default(),
254 sub_components: Default::default(),
255 property_init: Default::default(),
256 change_callbacks: Default::default(),
257 animations: Default::default(),
258 two_way_bindings: Default::default(),
259 const_properties: Default::default(),
260 init_code: Default::default(),
261 geometries: Default::default(),
262 layout_info_h: super::Expression::BoolLiteral(false).into(),
264 layout_info_v: super::Expression::BoolLiteral(false).into(),
265 child_of_layout: component.root_element.borrow().child_of_layout,
266 grid_layout_input_for_repeated: None,
267 is_repeated_row: component
268 .root_element
269 .borrow()
270 .grid_layout_cell
271 .as_ref()
272 .is_some_and(|c| c.borrow().child_items.is_some()),
273 grid_layout_children: Default::default(),
274 accessible_prop: Default::default(),
275 element_infos: Default::default(),
276 prop_analysis: Default::default(),
277 };
278 let mut mapping = LoweredSubComponentMapping::default();
279 let mut repeated = TiVec::new();
280 let mut accessible_prop = Vec::new();
281 let mut change_callbacks = Vec::new();
282
283 if let Some(parent) = component.parent_element.upgrade() {
284 if parent.borrow().repeated.as_ref().is_some_and(|x| !x.is_conditional_element) {
286 sub_component.properties.push(Property {
287 name: "model_data".into(),
288 ty: crate::expression_tree::Expression::RepeaterModelReference {
289 element: component.parent_element.clone(),
290 }
291 .ty(),
292 ..Property::default()
293 });
294 sub_component.properties.push(Property {
295 name: "model_index".into(),
296 ty: Type::Int32,
297 ..Property::default()
298 });
299 }
300 };
301
302 let s: Option<ElementRc> = None;
303 let mut repeater_offset = 0;
304 crate::object_tree::recurse_elem(&component.root_element, &s, &mut |element, parent| {
305 let elem = element.borrow();
306 for (p, x) in &elem.property_declarations {
307 if x.is_alias.is_some() {
308 continue;
309 }
310 let reference = if let Type::Function(function) = &x.property_type {
311 let index = sub_component.functions.push_and_get_key(Function {
314 name: p.clone(),
315 ret_ty: function.return_type.clone(),
316 args: function.args.clone(),
317 code: super::Expression::CodeBlock(Vec::new()),
319 });
320 index.into()
321 } else if let Type::Callback(callback) = &x.property_type {
322 let index = sub_component.callbacks.push_and_get_key(Callback {
323 name: format_smolstr!("{}_{}", elem.id, p),
324 ret_ty: callback.return_type.clone(),
325 args: callback.args.clone(),
326 ty: Type::Callback(callback.clone()),
327 use_count: 0.into(),
328 });
329 index.into()
330 } else {
331 let index = sub_component.properties.push_and_get_key(Property {
332 name: format_smolstr!("{}_{}", elem.id, p),
333 ty: x.property_type.clone(),
334 ..Property::default()
335 });
336 index.into()
337 };
338 mapping.property_mapping.insert(
339 NamedReference::new(element, p.clone()),
340 MemberReference::Relative {
341 parent_level: 0,
342 local_reference: LocalMemberReference {
343 sub_component_path: Vec::new(),
344 reference,
345 },
346 },
347 );
348 }
349 if elem.repeated.is_some() {
350 let parent = if elem.is_component_placeholder { parent.clone() } else { None };
351
352 mapping.element_mapping.insert(
353 element.clone().into(),
354 LoweredElement::Repeated {
355 repeated_index: repeated.push_and_get_key((element.clone(), parent)),
356 },
357 );
358 mapping.repeater_count += 1;
359 return None;
360 }
361 match &elem.base_type {
362 ElementType::Component(comp) => {
363 let ty = state.sub_component_idx(comp);
364 let sub_component_index =
365 sub_component.sub_components.push_and_get_key(SubComponentInstance {
366 ty,
367 name: elem.id.clone(),
368 index_in_tree: *elem.item_index.get().unwrap(),
369 index_of_first_child_in_tree: *elem
370 .item_index_of_first_children
371 .get()
372 .unwrap(),
373 repeater_offset,
374 });
375 mapping.element_mapping.insert(
376 element.clone().into(),
377 LoweredElement::SubComponent { sub_component_index },
378 );
379 repeater_offset += comp.repeater_count();
380 }
381
382 ElementType::Native(n) => {
383 let item_index = sub_component.items.push_and_get_key(Item {
384 ty: n.clone(),
385 name: elem.id.clone(),
386 index_in_tree: *elem.item_index.get().unwrap(),
387 });
388 mapping
389 .element_mapping
390 .insert(element.clone().into(), LoweredElement::NativeItem { item_index });
391 }
392 _ => unreachable!(),
393 };
394 for (key, nr) in &elem.accessibility_props.0 {
395 let enum_value =
397 crate::generator::to_pascal_case(key.strip_prefix("accessible-").unwrap());
398 accessible_prop.push((*elem.item_index.get().unwrap(), enum_value, nr.clone()));
399 }
400
401 for (prop, expr) in &elem.change_callbacks {
402 change_callbacks
403 .push((NamedReference::new(element, prop.clone()), expr.borrow().clone()));
404 }
405
406 if compiler_config.debug_info {
407 let element_infos = elem.element_infos();
408 if !element_infos.is_empty() {
409 sub_component.element_infos.insert(*elem.item_index.get().unwrap(), element_infos);
410 }
411 }
412
413 Some(element.clone())
414 });
415
416 let inner = ExpressionLoweringCtxInner { mapping: &mapping, parent: parent_context, component };
417 let mut ctx = ExpressionLoweringCtx { inner, state };
418
419 crate::generator::handle_property_bindings_init(component, |e, p, binding| {
420 let nr = NamedReference::new(e, p.clone());
421 let prop = ctx.map_property_reference(&nr);
422
423 if let Type::Function { .. } = nr.ty() {
424 let MemberReference::Relative { parent_level, local_reference } = prop else {
425 unreachable!()
426 };
427 assert!(parent_level == 0);
428 assert!(local_reference.sub_component_path.is_empty());
429 let LocalMemberIndex::Function(function_index) = local_reference.reference else {
430 unreachable!()
431 };
432
433 sub_component.functions[function_index].code =
434 super::lower_expression::lower_expression(&binding.expression, &mut ctx);
435
436 return;
437 }
438
439 for tw in &binding.two_way_bindings {
440 sub_component.two_way_bindings.push((
441 prop.clone(),
442 ctx.map_property_reference(&tw.property),
443 tw.field_access.clone(),
444 ));
445 }
446 if !matches!(binding.expression, tree_Expression::Invalid) {
447 let expression =
448 super::lower_expression::lower_expression(&binding.expression, &mut ctx).into();
449
450 let is_constant = binding.analysis.as_ref().is_some_and(|a| a.is_const);
451 let animation = binding
452 .animation
453 .as_ref()
454 .filter(|_| !is_constant)
455 .map(|a| super::lower_expression::lower_animation(a, &mut ctx));
456
457 sub_component.prop_analysis.insert(
458 prop.clone(),
459 PropAnalysis {
460 property_init: Some(sub_component.property_init.len()),
461 analysis: get_property_analysis(e, p),
462 },
463 );
464
465 let is_state_info = matches!(
466 e.borrow().lookup_property(p).property_type,
467 Type::Struct(s) if matches!(s.name, StructName::BuiltinPrivate(BuiltinPrivateStruct::StateInfo))
468 );
469
470 sub_component.property_init.push((
471 prop.clone(),
472 BindingExpression {
473 expression,
474 animation,
475 is_constant,
476 is_state_info,
477 use_count: 0.into(),
478 },
479 ));
480 }
481
482 if e.borrow()
483 .property_analysis
484 .borrow()
485 .get(p)
486 .is_none_or(|a| a.is_set || a.is_set_externally)
487 && let Some(anim) = binding.animation.as_ref()
488 {
489 match super::lower_expression::lower_animation(anim, &mut ctx) {
490 Animation::Static(anim) => {
491 sub_component.animations.insert(prop.local(), anim);
492 }
493 Animation::Transition(_) => {
494 }
496 }
497 }
498 });
499 sub_component.repeated = repeated
500 .into_iter()
501 .map(|(elem, parent)| {
502 lower_repeated_component(&elem, parent, &sub_component, &mut ctx, compiler_config)
503 })
504 .collect();
505 for s in &mut sub_component.sub_components {
506 s.repeater_offset +=
507 (sub_component.repeated.len() + sub_component.component_containers.len()) as u32;
508 }
509
510 sub_component.popup_windows = component
511 .popup_windows
512 .borrow()
513 .iter()
514 .map(|popup| lower_popup_component(popup, &mut ctx, compiler_config))
515 .collect();
516
517 sub_component.menu_item_trees = component
518 .menu_item_tree
519 .borrow()
520 .iter()
521 .map(|c| {
522 let sc = lower_sub_component(c, ctx.state, Some(&ctx.inner), compiler_config);
523 ItemTree {
524 tree: make_tree(ctx.state, &c.root_element, &sc, &[]),
525 root: ctx.state.push_sub_component(sc),
526 }
527 })
528 .collect();
529
530 sub_component.timers = component.timers.borrow().iter().map(|t| lower_timer(t, &ctx)).collect();
531
532 crate::generator::for_each_const_properties(component, |elem, n| {
533 let x = ctx.map_property_reference(&NamedReference::new(elem, n.clone()));
534 sub_component.prop_analysis.entry(x.clone()).or_insert_with(|| PropAnalysis {
536 property_init: None,
537 analysis: get_property_analysis(elem, n),
538 });
539 sub_component.const_properties.push(x.local());
540 });
541
542 sub_component.init_code = component
543 .init_code
544 .borrow()
545 .iter()
546 .map(|e| super::lower_expression::lower_expression(e, &mut ctx).into())
547 .collect();
548
549 sub_component.layout_info_h = super::lower_expression::get_layout_info(
550 &component.root_element,
551 &mut ctx,
552 &component.root_constraints.borrow(),
553 crate::layout::Orientation::Horizontal,
554 )
555 .into();
556 sub_component.layout_info_v = super::lower_expression::get_layout_info(
557 &component.root_element,
558 &mut ctx,
559 &component.root_constraints.borrow(),
560 crate::layout::Orientation::Vertical,
561 )
562 .into();
563 if let Some(grid_layout_cell) = component.root_element.borrow().grid_layout_cell.as_ref() {
564 sub_component.grid_layout_input_for_repeated = Some(
565 super::lower_expression::get_grid_layout_input_for_repeated(
566 &mut ctx,
567 &grid_layout_cell.borrow(),
568 )
569 .into(),
570 );
571 let children_constraints =
573 grid_layout_cell.borrow().child_items.clone().unwrap_or_default();
574 for layout_item in children_constraints {
575 let layout_info_h = super::lower_expression::get_layout_info(
576 &layout_item.element,
577 &mut ctx,
578 &layout_item.constraints,
579 crate::layout::Orientation::Horizontal,
580 );
581 let layout_info_v = super::lower_expression::get_layout_info(
582 &layout_item.element,
583 &mut ctx,
584 &layout_item.constraints,
585 crate::layout::Orientation::Vertical,
586 );
587 sub_component.grid_layout_children.push(super::GridLayoutChildLayoutInfo {
588 layout_info_h: layout_info_h.into(),
589 layout_info_v: layout_info_v.into(),
590 });
591 }
592 }
593
594 sub_component.accessible_prop = accessible_prop
595 .into_iter()
596 .map(|(idx, key, nr)| {
597 let prop = ctx.map_property_reference(&nr);
598 let expr = match nr.ty() {
599 Type::Bool => super::Expression::Condition {
600 condition: super::Expression::PropertyReference(prop).into(),
601 true_expr: super::Expression::StringLiteral("true".into()).into(),
602 false_expr: super::Expression::StringLiteral("false".into()).into(),
603 },
604 Type::Int32 | Type::Float32 => super::Expression::Cast {
605 from: super::Expression::PropertyReference(prop).into(),
606 to: Type::String,
607 },
608 Type::String => super::Expression::PropertyReference(prop),
609 Type::Enumeration(e) if e.name == "AccessibleRole" => {
610 super::Expression::PropertyReference(prop)
611 }
612 Type::Callback(callback) => super::Expression::CallBackCall {
613 callback: prop,
614 arguments: (0..callback.args.len())
615 .map(|index| super::Expression::FunctionParameterReference { index })
616 .collect(),
617 },
618 _ => panic!("Invalid type for accessible property"),
619 };
620
621 ((idx, key), expr.into())
622 })
623 .collect();
624
625 sub_component.change_callbacks = change_callbacks
626 .into_iter()
627 .map(|(nr, exprs)| {
628 let prop = ctx.map_property_reference(&nr);
629 let expr = super::lower_expression::lower_expression(
630 &tree_Expression::CodeBlock(exprs),
631 &mut ctx,
632 );
633 (prop, expr.into())
634 })
635 .collect();
636
637 crate::object_tree::recurse_elem(&component.root_element, &(), &mut |element, _| {
638 let elem = element.borrow();
639 if elem.repeated.is_some() {
640 return;
641 };
642 let Some(geom) = &elem.geometry_props else { return };
643 let item_index = *elem.item_index.get().unwrap() as usize;
644 if item_index >= sub_component.geometries.len() {
645 sub_component.geometries.resize(item_index + 1, Default::default());
646 }
647 sub_component.geometries[item_index] = Some(lower_geometry(geom, &ctx).into());
648 });
649
650 LoweredSubComponent { sub_component, mapping }
651}
652
653fn lower_geometry(
654 geom: &crate::object_tree::GeometryProps,
655 ctx: &ExpressionLoweringCtx<'_>,
656) -> super::Expression {
657 let mut fields = BTreeMap::default();
658 let mut values = BTreeMap::default();
659 for (f, v) in [("x", &geom.x), ("y", &geom.y), ("width", &geom.width), ("height", &geom.height)]
660 {
661 fields.insert(f.into(), Type::LogicalLength);
662 values
663 .insert(f.into(), super::Expression::PropertyReference(ctx.map_property_reference(v)));
664 }
665 super::Expression::Struct { ty: Rc::new(Struct { fields, name: StructName::None }), values }
666}
667
668fn get_property_analysis(elem: &ElementRc, p: &str) -> crate::object_tree::PropertyAnalysis {
669 let mut a = elem.borrow().property_analysis.borrow().get(p).cloned().unwrap_or_default();
670 let mut elem = elem.clone();
671 loop {
672 if let Some(d) = elem.borrow().property_declarations.get(p) {
673 if let Some(nr) = &d.is_alias {
674 a.merge(&get_property_analysis(&nr.element(), nr.name()));
675 }
676 return a;
677 }
678 let base = elem.borrow().base_type.clone();
679 match base {
680 ElementType::Native(n) => {
681 if n.properties.get(p).is_some_and(|p| p.is_native_output()) {
682 a.is_set = true;
683 }
684 }
685 ElementType::Component(c) => {
686 elem = c.root_element.clone();
687 if let Some(a2) = elem.borrow().property_analysis.borrow().get(p) {
688 a.merge_with_base(a2);
689 }
690 continue;
691 }
692 _ => (),
693 };
694 return a;
695 }
696}
697
698fn lower_repeated_component(
699 elem: &ElementRc,
700 parent_component_container: Option<ElementRc>,
701 sub_component: &SubComponent,
702 ctx: &mut ExpressionLoweringCtx,
703 compiler_config: &CompilerConfiguration,
704) -> RepeatedElement {
705 let e = elem.borrow();
706 let component = e.base_type.as_component().clone();
707 let repeated = e.repeated.as_ref().unwrap();
708
709 let sc = lower_sub_component(&component, ctx.state, Some(&ctx.inner), compiler_config);
710
711 let listview = repeated.is_listview.as_ref().map(|lv| {
712 let geom = component.root_element.borrow().geometry_props.clone().unwrap();
713 ListViewInfo {
714 viewport_y: ctx.map_property_reference(&lv.viewport_y).local().clone(),
715 viewport_height: ctx.map_property_reference(&lv.viewport_height).local().clone(),
716 viewport_width: ctx.map_property_reference(&lv.viewport_width).local().clone(),
717 listview_height: ctx.map_property_reference(&lv.listview_height).local().clone(),
718 listview_width: ctx.map_property_reference(&lv.listview_width).local().clone(),
719 prop_y: sc.mapping.map_property_reference(&geom.y, ctx.state),
720 prop_height: sc.mapping.map_property_reference(&geom.height, ctx.state),
721 }
722 });
723
724 let parent_index = parent_component_container.map(|p| *p.borrow().item_index.get().unwrap());
725 let container_item_index =
726 parent_index.and_then(|pii| sub_component.items.position(|i| i.index_in_tree == pii));
727
728 RepeatedElement {
729 model: super::lower_expression::lower_expression(&repeated.model, ctx).into(),
730 sub_tree: ItemTree {
731 tree: make_tree(ctx.state, &component.root_element, &sc, &[]),
732 root: ctx.state.push_sub_component(sc),
733 },
734 index_prop: (!repeated.is_conditional_element).then_some(PropertyIdx::REPEATER_INDEX),
735 data_prop: (!repeated.is_conditional_element).then_some(PropertyIdx::REPEATER_DATA),
736 index_in_tree: *e.item_index.get().unwrap(),
737 listview,
738 container_item_index,
739 }
740}
741
742fn lower_popup_component(
743 popup: &object_tree::PopupWindow,
744 ctx: &mut ExpressionLoweringCtx,
745 compiler_config: &CompilerConfiguration,
746) -> PopupWindow {
747 let sc = lower_sub_component(&popup.component, ctx.state, Some(&ctx.inner), compiler_config);
748 use super::Expression::PropertyReference as PR;
749 let position = super::lower_expression::make_struct(
750 BuiltinPublicStruct::LogicalPosition,
751 [
752 ("x", Type::LogicalLength, PR(sc.mapping.map_property_reference(&popup.x, ctx.state))),
753 ("y", Type::LogicalLength, PR(sc.mapping.map_property_reference(&popup.y, ctx.state))),
754 ],
755 );
756
757 let item_tree = ItemTree {
758 tree: make_tree(ctx.state, &popup.component.root_element, &sc, &[]),
759 root: ctx.state.push_sub_component(sc),
760 };
761 PopupWindow { item_tree, position: position.into() }
762}
763
764fn lower_timer(timer: &object_tree::Timer, ctx: &ExpressionLoweringCtx) -> Timer {
765 Timer {
766 interval: super::Expression::PropertyReference(ctx.map_property_reference(&timer.interval))
767 .into(),
768 running: super::Expression::PropertyReference(ctx.map_property_reference(&timer.running))
769 .into(),
770 triggered: super::Expression::CallBackCall {
772 callback: ctx.map_property_reference(&timer.triggered),
773 arguments: Vec::new(),
774 }
775 .into(),
776 }
777}
778
779fn lower_global(
781 global: &Rc<Component>,
782 global_index: GlobalIdx,
783 state: &mut LoweringState,
784) -> GlobalComponent {
785 let mut properties = TiVec::new();
786 let mut callbacks = TiVec::new();
787 let mut const_properties = TiVec::new();
788 let mut prop_analysis = TiVec::new();
789 let mut functions = TiVec::new();
790
791 for (p, x) in &global.root_element.borrow().property_declarations {
792 if x.is_alias.is_some() {
793 continue;
794 }
795 let nr = NamedReference::new(&global.root_element, p.clone());
796
797 if let Type::Function(function) = &x.property_type {
798 let function_index: FunctionIdx = functions.push_and_get_key(Function {
800 name: p.clone(),
801 ret_ty: function.return_type.clone(),
802 args: function.args.clone(),
803 code: super::Expression::CodeBlock(Vec::new()),
805 });
806 state.global_properties.insert(
807 nr.clone(),
808 MemberReference::Global { global_index, member: function_index.into() },
809 );
810 continue;
811 } else if let Type::Callback(cb) = &x.property_type {
812 let callback_index: CallbackIdx = callbacks.push_and_get_key(Callback {
813 name: p.clone(),
814 ret_ty: cb.return_type.clone(),
815 args: cb.args.clone(),
816 ty: x.property_type.clone(),
817 use_count: 0.into(),
818 });
819 state.global_properties.insert(
820 nr.clone(),
821 MemberReference::Global { global_index, member: callback_index.into() },
822 );
823 continue;
824 }
825
826 let property_index: PropertyIdx = properties.push_and_get_key(Property {
827 name: p.clone(),
828 ty: x.property_type.clone(),
829 ..Property::default()
830 });
831
832 const_properties.push(nr.is_constant());
833
834 prop_analysis.push(
835 global
836 .root_element
837 .borrow()
838 .property_analysis
839 .borrow()
840 .get(p)
841 .cloned()
842 .unwrap_or_default(),
843 );
844 state.global_properties.insert(
845 nr.clone(),
846 MemberReference::Global { global_index, member: property_index.into() },
847 );
848 }
849
850 let is_builtin = if let Some(builtin) = global.root_element.borrow().native_class() {
851 for (p, x) in &builtin.properties {
853 let property_index = properties.push_and_get_key(Property {
854 name: p.clone(),
855 ty: x.ty.clone(),
856 ..Property::default()
857 });
858 let nr = NamedReference::new(&global.root_element, p.clone());
859 state.global_properties.insert(
860 nr,
861 MemberReference::Global { global_index, member: property_index.into() },
862 );
863 prop_analysis.push(PropertyAnalysis {
864 is_set_externally: true,
866 ..global
867 .root_element
868 .borrow()
869 .property_analysis
870 .borrow()
871 .get(p)
872 .cloned()
873 .unwrap_or_default()
874 });
875 }
876 true
877 } else {
878 false
879 };
880
881 GlobalComponent {
882 name: global.root_element.borrow().id.clone(),
883 init_values: BTreeMap::new(),
884 properties,
885 callbacks,
886 functions,
887 change_callbacks: BTreeMap::new(),
888 const_properties,
889 public_properties: Default::default(),
890 private_properties: global.private_properties.borrow().clone(),
891 exported: !global.exported_global_names.borrow().is_empty(),
892 aliases: global.global_aliases(),
893 is_builtin,
894 from_library: global.from_library.get(),
895 prop_analysis,
896 }
897}
898
899fn lower_global_expressions(
900 global: &Rc<Component>,
901 state: &mut LoweringState,
902 lowered: &mut GlobalComponent,
903) {
904 let mapping = LoweredSubComponentMapping::default();
906 let inner = ExpressionLoweringCtxInner { mapping: &mapping, parent: None, component: global };
907 let mut ctx = ExpressionLoweringCtx { inner, state };
908
909 for (prop, binding) in &global.root_element.borrow().bindings {
910 assert!(binding.borrow().two_way_bindings.is_empty());
911 assert!(binding.borrow().animation.is_none());
912 let expression =
913 super::lower_expression::lower_expression(&binding.borrow().expression, &mut ctx);
914
915 let nr = NamedReference::new(&global.root_element, prop.clone());
916 let member_index = match &ctx.state.global_properties[&nr] {
917 MemberReference::Global {
918 member: LocalMemberIndex::Function(function_index), ..
919 } => {
920 lowered.functions[*function_index].code = expression;
921 continue;
922 }
923 MemberReference::Global { member, .. } => member.clone(),
924 _ => unreachable!(),
925 };
926 let is_constant = binding.borrow().analysis.as_ref().is_some_and(|a| a.is_const);
927 lowered.init_values.insert(
928 member_index,
929 BindingExpression {
930 expression: expression.into(),
931 animation: None,
932 is_constant,
933 is_state_info: false,
934 use_count: 0.into(),
935 },
936 );
937 }
938
939 for (prop, expr) in &global.root_element.borrow().change_callbacks {
940 let nr = NamedReference::new(&global.root_element, prop.clone());
941 let MemberReference::Global { member: LocalMemberIndex::Property(property_index), .. } =
942 ctx.state.global_properties[&nr]
943 else {
944 unreachable!()
945 };
946 let expression = super::lower_expression::lower_expression(
947 &tree_Expression::CodeBlock(expr.borrow().clone()),
948 &mut ctx,
949 );
950 lowered.change_callbacks.insert(property_index, expression.into());
951 }
952
953 if let Some(builtin) = global.root_element.borrow().native_class() {
954 if lowered.exported {
955 lowered.public_properties = builtin
956 .properties
957 .iter()
958 .map(|(p, c)| {
959 let property_reference = mapping.map_property_reference(
960 &NamedReference::new(&global.root_element, p.clone()),
961 state,
962 );
963 PublicProperty {
964 name: p.clone(),
965 ty: c.ty.clone(),
966 prop: property_reference,
967 read_only: c.property_visibility == PropertyVisibility::Output,
968 }
969 })
970 .collect()
971 }
972 } else {
973 lowered.public_properties = public_properties(global, &mapping, state);
974 }
975}
976
977fn make_tree(
978 state: &LoweringState,
979 element: &ElementRc,
980 component: &LoweredSubComponent,
981 sub_component_path: &[SubComponentInstanceIdx],
982) -> TreeNode {
983 let e = element.borrow();
984 let children = e.children.iter().map(|c| make_tree(state, c, component, sub_component_path));
985 let repeater_count = component.mapping.repeater_count;
986 match component.mapping.element_mapping.get(&ByAddress(element.clone())).unwrap() {
987 LoweredElement::SubComponent { sub_component_index } => {
988 let sub_component = e.sub_component().unwrap();
989 let new_sub_component_path = sub_component_path
990 .iter()
991 .copied()
992 .chain(std::iter::once(*sub_component_index))
993 .collect::<Vec<_>>();
994 let mut tree_node = make_tree(
995 state,
996 &sub_component.root_element,
997 state.sub_component(sub_component),
998 &new_sub_component_path,
999 );
1000 tree_node.children.extend(children);
1001 tree_node.is_accessible |= !e.accessibility_props.0.is_empty();
1002 tree_node
1003 }
1004 LoweredElement::NativeItem { item_index } => TreeNode {
1005 is_accessible: !e.accessibility_props.0.is_empty(),
1006 sub_component_path: sub_component_path.into(),
1007 item_index: itertools::Either::Left(*item_index),
1008 children: children.collect(),
1009 },
1010 LoweredElement::Repeated { repeated_index } => TreeNode {
1011 is_accessible: false,
1012 sub_component_path: sub_component_path.into(),
1013 item_index: itertools::Either::Right(usize::from(*repeated_index) as u32),
1014 children: Vec::new(),
1015 },
1016 LoweredElement::ComponentPlaceholder { repeated_index } => TreeNode {
1017 is_accessible: false,
1018 sub_component_path: sub_component_path.into(),
1019 item_index: itertools::Either::Right(*repeated_index + repeater_count),
1020 children: Vec::new(),
1021 },
1022 }
1023}
1024
1025fn public_properties(
1026 component: &Component,
1027 mapping: &LoweredSubComponentMapping,
1028 state: &LoweringState,
1029) -> PublicProperties {
1030 component
1031 .root_element
1032 .borrow()
1033 .property_declarations
1034 .iter()
1035 .filter(|(_, c)| c.expose_in_public_api)
1036 .map(|(p, c)| {
1037 let property_reference = mapping.map_property_reference(
1038 &NamedReference::new(&component.root_element, p.clone()),
1039 state,
1040 );
1041 PublicProperty {
1042 name: p.clone(),
1043 ty: c.property_type.clone(),
1044 prop: property_reference,
1045 read_only: c.visibility == PropertyVisibility::Output,
1046 }
1047 })
1048 .collect()
1049}