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