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 let v_cross_constraint = component
621 .root_element
622 .borrow()
623 .layout_info_v_with_constraint
624 .is_some()
625 .then(|| {
626 super::lower_layout_expression::default_cross_axis_constraint(&component.root_element)
627 })
628 .flatten();
629 sub_component.layout_info_v = super::lower_layout_expression::get_layout_info(
630 &component.root_element,
631 &mut ctx,
632 &component.root_constraints.borrow(),
633 crate::layout::Orientation::Vertical,
634 v_cross_constraint,
635 )
636 .into();
637 if sub_component.child_of_layout {
639 let root_elem = &component.root_element;
640 let has_flex_binding =
641 ["flex-grow", "flex-shrink", "flex-basis", "flex-align-self", "flex-order"]
642 .iter()
643 .any(|name| crate::layout::binding_reference(root_elem, name).is_some());
644 if has_flex_binding {
645 sub_component.flexbox_layout_item_info_for_repeated = Some(
646 super::lower_layout_expression::get_flexbox_layout_item_info_for_repeated(
647 &mut ctx, root_elem,
648 )
649 .into(),
650 );
651 }
652 }
653
654 if let Some(grid_layout_cell) = component.root_element.borrow().grid_layout_cell.as_ref() {
655 let grid_cell_ref = grid_layout_cell.borrow();
656 sub_component.grid_layout_input_for_repeated = Some(
657 super::lower_layout_expression::get_grid_layout_input_for_repeated(
658 &mut ctx,
659 &grid_cell_ref,
660 )
661 .into(),
662 );
663
664 if let Some(children_constraints) = grid_cell_ref.child_items.as_ref() {
666 let mut row_child_templates = Vec::new();
667 for child_template in children_constraints.iter() {
668 match child_template {
669 crate::layout::RowChildTemplate::Static(layout_item) => {
670 let layout_info_h = super::lower_layout_expression::get_layout_info(
671 &layout_item.element,
672 &mut ctx,
673 &layout_item.constraints,
674 crate::layout::Orientation::Horizontal,
675 None,
676 );
677 let layout_info_v = super::lower_layout_expression::get_layout_info(
678 &layout_item.element,
679 &mut ctx,
680 &layout_item.constraints,
681 crate::layout::Orientation::Vertical,
682 None,
683 );
684 let child_index = sub_component.grid_layout_children.push_and_get_key(
685 super::GridLayoutChildLayoutInfo {
686 layout_info_h: layout_info_h.into(),
687 layout_info_v: layout_info_v.into(),
688 },
689 );
690 row_child_templates
691 .push(super::RowChildTemplateInfo::Static { child_index });
692 }
693 crate::layout::RowChildTemplate::Repeated { repeated_element, .. } => {
694 if let Some(super::lower_to_item_tree::LoweredElement::Repeated {
696 repeated_index,
697 }) = mapping.element_mapping.get(&repeated_element.clone().into())
698 {
699 row_child_templates.push(super::RowChildTemplateInfo::Repeated {
700 repeater_index: *repeated_index,
701 });
702 }
703 }
704 }
705 }
706 sub_component.row_child_templates = Some(row_child_templates);
710 }
711 }
712
713 sub_component.accessible_prop = accessible_prop
714 .into_iter()
715 .map(|(idx, key, nr)| {
716 let prop = ctx.map_property_reference(&nr);
717 let expr = match nr.ty() {
718 Type::Bool => super::Expression::Condition {
719 condition: super::Expression::PropertyReference(prop).into(),
720 true_expr: super::Expression::StringLiteral("true".into()).into(),
721 false_expr: super::Expression::StringLiteral("false".into()).into(),
722 },
723 Type::Int32 | Type::Float32 => super::Expression::Cast {
724 from: super::Expression::PropertyReference(prop).into(),
725 to: Type::String,
726 },
727 Type::String => super::Expression::PropertyReference(prop),
728 Type::Enumeration(ref e) if e.name == "AccessibleRole" => {
729 super::Expression::PropertyReference(prop)
730 }
731 Type::Enumeration(_) => super::Expression::Cast {
732 from: super::Expression::PropertyReference(prop).into(),
733 to: Type::String,
734 },
735 Type::Callback(callback) => super::Expression::CallBackCall {
736 callback: prop,
737 arguments: (0..callback.args.len())
738 .map(|index| super::Expression::FunctionParameterReference { index })
739 .collect(),
740 },
741 _ => panic!("Invalid type for accessible property"),
742 };
743
744 ((idx, key), expr.into())
745 })
746 .collect();
747
748 sub_component.change_callbacks = change_callbacks
749 .into_iter()
750 .map(|(nr, exprs)| {
751 let prop = ctx.map_property_reference(&nr);
752 let expr = super::lower_expression::lower_expression(
753 &tree_Expression::CodeBlock(exprs),
754 &mut ctx,
755 );
756 (prop, expr.into())
757 })
758 .collect();
759
760 crate::object_tree::recurse_elem(&component.root_element, &(), &mut |element, _| {
761 let elem = element.borrow();
762 if elem.repeated.is_some() {
763 return;
764 };
765 let Some(geom) = &elem.geometry_props else { return };
766 let item_index = *elem.item_index.get().unwrap() as usize;
767 if item_index >= sub_component.geometries.len() {
768 sub_component.geometries.resize(item_index + 1, Default::default());
769 }
770 sub_component.geometries[item_index] = Some(lower_geometry(geom, &ctx).into());
771 });
772
773 LoweredSubComponent { sub_component, mapping }
774}
775
776fn lower_geometry(
777 geom: &crate::object_tree::GeometryProps,
778 ctx: &ExpressionLoweringCtx<'_>,
779) -> super::Expression {
780 let mut fields = BTreeMap::default();
781 let mut values = BTreeMap::default();
782 for (f, v) in [("x", &geom.x), ("y", &geom.y), ("width", &geom.width), ("height", &geom.height)]
783 {
784 fields.insert(f.into(), Type::LogicalLength);
785 values
786 .insert(f.into(), super::Expression::PropertyReference(ctx.map_property_reference(v)));
787 }
788 super::Expression::Struct { ty: Rc::new(Struct { fields, name: StructName::None }), values }
789}
790
791fn get_property_analysis(elem: &ElementRc, p: &str) -> crate::object_tree::PropertyAnalysis {
792 let mut a = elem.borrow().property_analysis.borrow().get(p).cloned().unwrap_or_default();
793 let mut elem = elem.clone();
794 loop {
795 if let Some(d) = elem.borrow().property_declarations.get(p) {
796 if let Some(nr) = &d.is_alias {
797 a.merge(&get_property_analysis(&nr.element(), nr.name()));
798 }
799 return a;
800 }
801 let base = elem.borrow().base_type.clone();
802 match base {
803 ElementType::Native(n) if n.properties.get(p).is_some_and(|p| p.is_native_output()) => {
804 a.is_set = true;
805 }
806 ElementType::Component(c) => {
807 elem = c.root_element.clone();
808 if let Some(a2) = elem.borrow().property_analysis.borrow().get(p) {
809 a.merge_with_base(a2);
810 }
811 continue;
812 }
813 _ => (),
814 };
815 return a;
816 }
817}
818
819fn lower_repeated_component(
820 elem: &ElementRc,
821 parent_component_container: Option<ElementRc>,
822 sub_component: &SubComponent,
823 ctx: &mut ExpressionLoweringCtx,
824 compiler_config: &CompilerConfiguration,
825) -> RepeatedElement {
826 let e = elem.borrow();
827 let component = e.base_type.as_component().clone();
828 let repeated = e.repeated.as_ref().unwrap();
829
830 let sc = lower_sub_component(&component, ctx.state, Some(&ctx.inner), compiler_config);
831
832 let listview = repeated.is_listview.as_ref().map(|lv| {
833 let geom = component.root_element.borrow().geometry_props.clone().unwrap();
834 ListViewInfo {
835 viewport_y: ctx.map_property_reference(&lv.viewport_y),
836 viewport_height: ctx.map_property_reference(&lv.viewport_height),
837 viewport_width: ctx.map_property_reference(&lv.viewport_width),
838 listview_height: ctx.map_property_reference(&lv.listview_height),
839 listview_width: ctx.map_property_reference(&lv.listview_width),
840 prop_y: sc.mapping.map_property_reference(&geom.y, ctx.state),
841 prop_height: sc.mapping.map_property_reference(&geom.height, ctx.state),
842 }
843 });
844
845 let parent_index = parent_component_container.map(|p| *p.borrow().item_index.get().unwrap());
846 let container_item_index =
847 parent_index.and_then(|pii| sub_component.items.position(|i| i.index_in_tree == pii));
848
849 let tree = make_tree(ctx.state, &component.root_element, &sc, &[]);
850 let root = ctx.state.push_sub_component(sc);
851 ctx.state.sub_component_mapping.insert(ByAddress(component.clone()), root);
853
854 RepeatedElement {
855 model: super::lower_expression::lower_expression(&repeated.model, ctx).into(),
856 sub_tree: ItemTree { tree, root },
857 index_prop: (!repeated.is_conditional_element).then_some(PropertyIdx::REPEATER_INDEX),
858 data_prop: (!repeated.is_conditional_element).then_some(PropertyIdx::REPEATER_DATA),
859 index_in_tree: *e.item_index.get().unwrap(),
860 listview,
861 container_item_index,
862 }
863}
864
865fn lower_popup_component(
866 popup: &object_tree::PopupWindow,
867 ctx: &mut ExpressionLoweringCtx,
868 compiler_config: &CompilerConfiguration,
869) -> PopupWindow {
870 let sc = lower_sub_component(&popup.component, ctx.state, Some(&ctx.inner), compiler_config);
871 use super::Expression::PropertyReference as PR;
872 let position = super::lower_expression::make_struct(
873 BuiltinStruct::LogicalPosition,
874 [
875 ("x", Type::LogicalLength, PR(sc.mapping.map_property_reference(&popup.x, ctx.state))),
876 ("y", Type::LogicalLength, PR(sc.mapping.map_property_reference(&popup.y, ctx.state))),
877 ],
878 );
879
880 let item_tree = ItemTree {
881 tree: make_tree(ctx.state, &popup.component.root_element, &sc, &[]),
882 root: ctx.state.push_sub_component(sc),
883 };
884 PopupWindow { item_tree, position: position.into(), is_tooltip: popup.is_tooltip }
885}
886
887fn lower_timer(timer: &object_tree::Timer, ctx: &ExpressionLoweringCtx) -> Timer {
888 Timer {
889 interval: super::Expression::PropertyReference(ctx.map_property_reference(&timer.interval))
890 .into(),
891 running: super::Expression::PropertyReference(ctx.map_property_reference(&timer.running))
892 .into(),
893 triggered: super::Expression::CallBackCall {
895 callback: ctx.map_property_reference(&timer.triggered),
896 arguments: Vec::new(),
897 }
898 .into(),
899 }
900}
901
902fn lower_global(
904 global: &Rc<Component>,
905 global_index: GlobalIdx,
906 state: &mut LoweringState,
907) -> GlobalComponent {
908 let mut properties = TiVec::new();
909 let mut callbacks = TiVec::new();
910 let mut const_properties = TiVec::new();
911 let mut prop_analysis = TiVec::new();
912 let mut functions = TiVec::new();
913
914 for (p, x) in &global.root_element.borrow().property_declarations {
915 if x.is_alias.is_some() {
916 continue;
917 }
918 let nr = NamedReference::new(&global.root_element, p.clone());
919
920 if let Type::Function(function) = &x.property_type {
921 let function_index: FunctionIdx = functions.push_and_get_key(Function {
923 name: p.clone(),
924 ret_ty: function.return_type.clone(),
925 args: function.args.clone(),
926 code: super::Expression::CodeBlock(Vec::new()),
928 });
929 state.global_properties.insert(
930 nr.clone(),
931 MemberReference::Global { global_index, member: function_index.into() },
932 );
933 continue;
934 } else if let Type::Callback(cb) = &x.property_type {
935 let callback_index: CallbackIdx = callbacks.push_and_get_key(Callback {
936 name: p.clone(),
937 ret_ty: cb.return_type.clone(),
938 args: cb.args.clone(),
939 ty: x.property_type.clone(),
940 use_count: 0.into(),
941 needs_tracker: x.expose_in_public_api,
942 });
943 state.global_properties.insert(
944 nr.clone(),
945 MemberReference::Global { global_index, member: callback_index.into() },
946 );
947 continue;
948 }
949
950 let property_index: PropertyIdx = properties.push_and_get_key(Property {
951 name: p.clone(),
952 ty: x.property_type.clone(),
953 ..Property::default()
954 });
955
956 const_properties.push(nr.is_constant());
957
958 prop_analysis.push(
959 global
960 .root_element
961 .borrow()
962 .property_analysis
963 .borrow()
964 .get(p)
965 .cloned()
966 .unwrap_or_default(),
967 );
968 state.global_properties.insert(
969 nr.clone(),
970 MemberReference::Global { global_index, member: property_index.into() },
971 );
972 }
973
974 let is_builtin = if let Some(builtin) = global.root_element.borrow().native_class() {
975 for (p, x) in &builtin.properties {
977 let property_index = properties.push_and_get_key(Property {
978 name: p.clone(),
979 ty: x.ty.clone(),
980 ..Property::default()
981 });
982 let nr = NamedReference::new(&global.root_element, p.clone());
983 state.global_properties.insert(
984 nr,
985 MemberReference::Global { global_index, member: property_index.into() },
986 );
987 prop_analysis.push(PropertyAnalysis {
988 is_set_externally: true,
990 ..global
991 .root_element
992 .borrow()
993 .property_analysis
994 .borrow()
995 .get(p)
996 .cloned()
997 .unwrap_or_default()
998 });
999 }
1000 true
1001 } else {
1002 false
1003 };
1004
1005 GlobalComponent {
1006 name: global.root_element.borrow().id.clone(),
1007 init_values: BTreeMap::new(),
1008 properties,
1009 callbacks,
1010 functions,
1011 change_callbacks: BTreeMap::new(),
1012 const_properties,
1013 public_properties: Default::default(),
1014 private_properties: global.private_properties.borrow().clone(),
1015 exported: !global.exported_global_names.borrow().is_empty(),
1016 aliases: global.global_aliases(),
1017 is_builtin,
1018 from_library: global.from_library.get(),
1019 prop_analysis,
1020 }
1021}
1022
1023fn lower_global_expressions(
1024 global: &Rc<Component>,
1025 state: &mut LoweringState,
1026 lowered: &mut GlobalComponent,
1027) {
1028 let mapping = LoweredSubComponentMapping::default();
1030 let inner = ExpressionLoweringCtxInner { mapping: &mapping, parent: None, component: global };
1031 let mut ctx = ExpressionLoweringCtx { inner, state };
1032
1033 for (prop, binding) in &global.root_element.borrow().bindings {
1034 assert!(binding.borrow().two_way_bindings.is_empty());
1035 assert!(binding.borrow().animation.is_none());
1036 let expression =
1037 super::lower_expression::lower_expression(&binding.borrow().expression, &mut ctx);
1038
1039 let nr = NamedReference::new(&global.root_element, prop.clone());
1040 let member_index = match &ctx.state.global_properties[&nr] {
1041 MemberReference::Global {
1042 member: LocalMemberIndex::Function(function_index), ..
1043 } => {
1044 lowered.functions[*function_index].code = expression;
1045 continue;
1046 }
1047 MemberReference::Global { member, .. } => member.clone(),
1048 _ => unreachable!(),
1049 };
1050 let is_constant = binding.borrow().analysis.as_ref().is_some_and(|a| a.is_const);
1051 lowered.init_values.insert(
1052 member_index,
1053 BindingExpression {
1054 expression: expression.into(),
1055 animation: None,
1056 is_constant,
1057 is_state_info: false,
1058 use_count: 0.into(),
1059 },
1060 );
1061 }
1062
1063 for (prop, expr) in &global.root_element.borrow().change_callbacks {
1064 let nr = NamedReference::new(&global.root_element, prop.clone());
1065 let MemberReference::Global { member: LocalMemberIndex::Property(property_index), .. } =
1066 ctx.state.global_properties[&nr]
1067 else {
1068 unreachable!()
1069 };
1070 let expression = super::lower_expression::lower_expression(
1071 &tree_Expression::CodeBlock(expr.borrow().clone()),
1072 &mut ctx,
1073 );
1074 lowered.change_callbacks.insert(property_index, expression.into());
1075 }
1076
1077 if let Some(builtin) = global.root_element.borrow().native_class() {
1078 if lowered.exported {
1079 lowered.public_properties = builtin
1080 .properties
1081 .iter()
1082 .map(|(p, c)| {
1083 let property_reference = mapping.map_property_reference(
1084 &NamedReference::new(&global.root_element, p.clone()),
1085 state,
1086 );
1087 PublicProperty {
1088 name: p.clone(),
1089 ty: c.ty.clone(),
1090 prop: property_reference,
1091 read_only: c.property_visibility == PropertyVisibility::Output,
1092 }
1093 })
1094 .collect()
1095 }
1096 } else {
1097 lowered.public_properties = public_properties(global, &mapping, state);
1098 }
1099}
1100
1101fn make_tree(
1102 state: &LoweringState,
1103 element: &ElementRc,
1104 component: &LoweredSubComponent,
1105 sub_component_path: &[SubComponentInstanceIdx],
1106) -> TreeNode {
1107 let e = element.borrow();
1108 let children = e.children.iter().map(|c| make_tree(state, c, component, sub_component_path));
1109 let repeater_count = component.mapping.repeater_count;
1110 match component.mapping.element_mapping.get(&ByAddress(element.clone())).unwrap() {
1111 LoweredElement::SubComponent { sub_component_index } => {
1112 let sub_component = e.sub_component().unwrap();
1113 let new_sub_component_path = sub_component_path
1114 .iter()
1115 .copied()
1116 .chain(std::iter::once(*sub_component_index))
1117 .collect::<Vec<_>>();
1118 let mut tree_node = make_tree(
1119 state,
1120 &sub_component.root_element,
1121 state.sub_component(sub_component),
1122 &new_sub_component_path,
1123 );
1124 tree_node.children.extend(children);
1125 tree_node.is_accessible |= !e.accessibility_props.0.is_empty();
1126 tree_node
1127 }
1128 LoweredElement::NativeItem { item_index } => TreeNode {
1129 is_accessible: !e.accessibility_props.0.is_empty(),
1130 sub_component_path: sub_component_path.into(),
1131 item_index: itertools::Either::Left(*item_index),
1132 children: children.collect(),
1133 },
1134 LoweredElement::Repeated { repeated_index } => TreeNode {
1135 is_accessible: false,
1136 sub_component_path: sub_component_path.into(),
1137 item_index: itertools::Either::Right(usize::from(*repeated_index) as u32),
1138 children: Vec::new(),
1139 },
1140 LoweredElement::ComponentPlaceholder { repeated_index } => TreeNode {
1141 is_accessible: false,
1142 sub_component_path: sub_component_path.into(),
1143 item_index: itertools::Either::Right(*repeated_index + repeater_count),
1144 children: Vec::new(),
1145 },
1146 }
1147}
1148
1149fn public_properties(
1150 component: &Component,
1151 mapping: &LoweredSubComponentMapping,
1152 state: &LoweringState,
1153) -> PublicProperties {
1154 component
1155 .root_element
1156 .borrow()
1157 .property_declarations
1158 .iter()
1159 .filter(|(_, c)| c.expose_in_public_api)
1160 .map(|(p, c)| {
1161 let property_reference = mapping.map_property_reference(
1162 &NamedReference::new(&component.root_element, p.clone()),
1163 state,
1164 );
1165 PublicProperty {
1166 name: p.clone(),
1167 ty: c.property_type.clone(),
1168 prop: property_reference,
1169 read_only: c.visibility == PropertyVisibility::Output,
1170 }
1171 })
1172 .collect()
1173}