1use by_address::ByAddress;
5use itertools::Either;
6use std::sync::Arc;
7
8use super::lower_expression::{ExpressionLoweringCtx, ExpressionLoweringCtxInner};
9use crate::CompilerConfiguration;
10use crate::expression_tree::Expression as tree_Expression;
11use crate::langtype::{BuiltinStruct, ElementType, PropertyLookupMode, Struct, StructName, Type};
12use crate::llr::item_tree::*;
13use crate::namedreference::NamedReference;
14use crate::object_tree::{self, Component, ElementRc, PropertyAnalysis};
15use smol_str::{SmolStr, format_smolstr};
16use std::collections::{BTreeMap, HashMap};
17use std::rc::Rc;
18use typed_index_collections::TiVec;
19
20fn type_exports(document: &object_tree::Document) -> Vec<TypeExport> {
25 let used_types = document.used_types.borrow();
26 let public = public_facing_type_names(document);
27 let mut list = Vec::new();
28 for ty in &used_types.structs_and_enums {
29 let name = match ty {
30 Type::Struct(s) => match &s.name {
31 StructName::User { name, .. } => Some(name.clone()),
32 _ => None,
33 },
34 Type::Enumeration(en) => Some(en.name.clone()),
35 _ => None,
36 };
37 let Some(name) = name else { continue };
38 if used_types.collision_renamed_names.contains(&name) {
39 continue;
40 }
41 let deprecated = !public.contains(&name);
42 list.push(TypeExport { exported_name: name.clone(), internal_name: name, deprecated });
43 }
44 for (internal_name, exported_name) in document.exports.named_type_aliases() {
45 list.push(TypeExport { exported_name, internal_name, deprecated: false });
46 }
47 for (old_name, new_name) in &used_types.deprecated_type_aliases {
48 list.push(TypeExport {
49 exported_name: old_name.clone(),
50 internal_name: new_name.clone(),
51 deprecated: true,
52 });
53 }
54 list
55}
56
57fn public_facing_type_names(
61 document: &object_tree::Document,
62) -> std::collections::HashSet<SmolStr> {
63 let mut public = std::collections::HashSet::new();
64 let mut collect = |ty: &Type| {
65 crate::langtype::visit_declared_types(ty, &mut |name, _| {
66 public.insert(name.clone());
67 })
68 };
69 for (_, export) in document.exports.iter() {
70 match export {
71 Either::Right(ty) => collect(ty),
72 Either::Left(component) => {
73 for pd in component.root_element.borrow().property_declarations.values() {
74 if pd.visibility != object_tree::PropertyVisibility::Private {
75 collect(&pd.property_type);
76 }
77 }
78 }
79 }
80 }
81 public
82}
83
84pub fn lower_to_item_tree(
85 document: &crate::object_tree::Document,
86 compiler_config: &CompilerConfiguration,
87) -> CompilationUnit {
88 let mut state = LoweringState::default();
89
90 #[cfg(feature = "bundle-translations")]
91 {
92 state.translation_builder = document.translation_builder.clone();
93 }
94
95 let mut globals = TiVec::new();
96 for g in &document.used_types.borrow().globals {
97 let count = globals.next_key();
98 globals.push(lower_global(g, count, &mut state));
99 }
100 for (g, l) in document.used_types.borrow().globals.iter().zip(&mut globals) {
101 lower_global_expressions(g, &mut state, l);
102 }
103
104 for c in &document.used_types.borrow().sub_components {
105 let sc = lower_sub_component(c, &mut state, None, compiler_config);
106 let idx = state.push_sub_component(sc);
107 state.sub_component_mapping.insert(ByAddress(c.clone()), idx);
108 }
109
110 let public_components = document
111 .exported_roots()
112 .map(|component| {
113 let top_level_type = if component.inherits_system_tray_icon() {
114 TopLevelComponentType::SystemTrayIcon
115 } else {
116 TopLevelComponentType::Window
117 };
118 let mut sc = lower_sub_component(&component, &mut state, None, compiler_config);
119 let public_properties = public_properties(&component, &sc.mapping, &state);
120 sc.sub_component.name = component.id.clone();
121 let item_tree = ItemTree {
122 tree: make_tree(&state, &component.root_element, &sc, &[]),
123 root: state.push_sub_component(sc),
124 };
125 PublicComponent {
127 item_tree,
128 public_properties,
129 private_properties: component.private_properties.borrow().clone(),
130 name: component.id.clone(),
131 top_level_type,
132 }
133 })
134 .collect();
135
136 let popup_menu = document.popup_menu_impl.as_ref().map(|c| {
137 let sc = lower_sub_component(c, &mut state, None, compiler_config);
138 let sub_menu = sc.mapping.map_property_reference(
139 &NamedReference::new(&c.root_element, SmolStr::new_static("sub-menu")),
140 &state,
141 );
142 let activated = sc.mapping.map_property_reference(
143 &NamedReference::new(&c.root_element, SmolStr::new_static("activated")),
144 &state,
145 );
146 let close = sc.mapping.map_property_reference(
147 &NamedReference::new(&c.root_element, SmolStr::new_static("close-popup")),
148 &state,
149 );
150 let entries = sc.mapping.map_property_reference(
151 &NamedReference::new(&c.root_element, SmolStr::new_static("entries")),
152 &state,
153 );
154 let item_tree = ItemTree {
155 tree: make_tree(&state, &c.root_element, &sc, &[]),
156 root: state.push_sub_component(sc),
157 };
158 PopupMenu { item_tree, sub_menu, activated, close, entries }
159 });
160
161 let mut root = CompilationUnit {
162 public_components,
163 globals,
164 sub_components: state.sub_components.into_iter().map(|sc| sc.sub_component).collect(),
165 used_sub_components: document
166 .used_types
167 .borrow()
168 .sub_components
169 .iter()
170 .map(|tree_sub_compo| state.sub_component_mapping[&ByAddress(tree_sub_compo.clone())])
171 .collect(),
172 has_debug_info: compiler_config.debug_info,
173 popup_menu,
174 type_exports: type_exports(document),
175 #[cfg(feature = "bundle-translations")]
176 translations: state.translation_builder.map(|x| x.result()),
177 };
178 super::optim_passes::run_passes(&mut root);
179 root
180}
181
182#[derive(Debug, Clone)]
183pub enum LoweredElement {
184 SubComponent { sub_component_index: SubComponentInstanceIdx },
185 NativeItem { item_index: ItemInstanceIdx },
186 Repeated { repeated_index: RepeatedElementIdx },
187 ComponentPlaceholder { repeated_index: u32 },
188}
189
190#[derive(Default, Debug, Clone)]
191pub struct LoweredSubComponentMapping {
192 pub element_mapping: HashMap<ByAddress<ElementRc>, LoweredElement>,
193 pub property_mapping: HashMap<NamedReference, MemberReference>,
194 pub repeater_count: u32,
195 pub container_count: u32,
196}
197
198impl LoweredSubComponentMapping {
199 pub fn map_property_reference(
200 &self,
201 from: &NamedReference,
202 state: &LoweringState,
203 ) -> MemberReference {
204 if let Some(x) = self.property_mapping.get(from) {
205 return x.clone();
206 }
207 if let Some(x) = state.global_properties.get(from) {
208 return x.clone();
209 }
210 let element = from.element();
211 if let Some(alias) = element
212 .borrow()
213 .property_declarations
214 .get(from.name())
215 .and_then(|x| x.is_alias.as_ref())
216 {
217 return self.map_property_reference(alias, state);
218 }
219 match self.element_mapping.get(&element.clone().into()).unwrap() {
220 LoweredElement::SubComponent { sub_component_index } => {
221 if let ElementType::Component(base) = &element.borrow().base_type {
222 let mut prop_ref = state.map_property_reference(&NamedReference::new(
223 &base.root_element,
224 from.name().clone(),
225 ));
226 if let MemberReference::Relative { parent_level, local_reference } =
227 &mut prop_ref
228 {
229 assert_eq!(*parent_level, 0, "the sub-component had no parents");
230 local_reference.sub_component_path.insert(0, *sub_component_index);
231 }
232 return prop_ref;
233 }
234 unreachable!()
235 }
236 LoweredElement::NativeItem { item_index } => {
237 let kind = match element
242 .borrow()
243 .lookup_property(from.name(), PropertyLookupMode::InternalName)
244 .property_type
245 {
246 Type::Callback(..) => super::NativeMemberKind::Callback,
247 Type::Function(..) => super::NativeMemberKind::Function,
248 _ => super::NativeMemberKind::Property,
249 };
250 MemberReference::Relative {
251 parent_level: 0,
252 local_reference: LocalMemberReference {
253 sub_component_path: Vec::new(),
254 reference: LocalMemberIndex::Native {
255 item_index: *item_index,
256 prop_name: from.name().clone(),
257 kind,
258 },
259 },
260 }
261 }
262 LoweredElement::Repeated { .. } => {
263 panic!(
264 "Trying to map property {from:?} on a repeated element {} of type {:?}",
265 element.borrow().id,
266 element.borrow().base_type
267 );
268 }
269 LoweredElement::ComponentPlaceholder { .. } => unreachable!(),
270 }
271 }
272}
273
274pub struct LoweredSubComponent {
275 sub_component: SubComponent,
276 mapping: LoweredSubComponentMapping,
277}
278
279#[derive(Default)]
280pub struct LoweringState {
281 global_properties: HashMap<NamedReference, MemberReference>,
282 sub_components: TiVec<SubComponentIdx, LoweredSubComponent>,
283 sub_component_mapping: HashMap<ByAddress<Rc<Component>>, SubComponentIdx>,
284 #[cfg(feature = "bundle-translations")]
285 pub translation_builder: Option<crate::translations::TranslationsBuilder>,
286 struct_assignment_count: usize,
290}
291
292impl LoweringState {
293 pub fn unique_struct_assignment_name(&mut self) -> SmolStr {
296 let n = self.struct_assignment_count;
297 self.struct_assignment_count += 1;
298 format_smolstr!("struct_assignment{n}")
299 }
300
301 pub fn map_property_reference(&self, from: &NamedReference) -> MemberReference {
302 if let Some(x) = self.global_properties.get(from) {
303 return x.clone();
304 }
305
306 let element = from.element();
307 let sc = self.sub_component(&element.borrow().enclosing_component.upgrade().unwrap());
308 sc.mapping.map_property_reference(from, self)
309 }
310
311 fn sub_component<'a>(&'a self, component: &Rc<Component>) -> &'a LoweredSubComponent {
312 &self.sub_components[self.sub_component_idx(component)]
313 }
314
315 pub fn row_child_templates(
319 &self,
320 component: &Rc<Component>,
321 ) -> Option<Vec<super::RowChildTemplateInfo>> {
322 self.sub_components[self.sub_component_idx(component)]
323 .sub_component
324 .row_child_templates
325 .clone()
326 }
327
328 fn sub_component_idx(&self, component: &Rc<Component>) -> SubComponentIdx {
329 *self.sub_component_mapping.get(&ByAddress(component.clone())).unwrap_or_else(|| {
330 debug_assert!(
331 false,
332 "no entry found for key: component id='{}', available keys: {:?}",
333 component.id,
334 self.sub_component_mapping.keys().map(|k| k.0.id.clone()).collect::<Vec<_>>()
335 );
336 unreachable!(
337 "component must be registered before querying sub_component_idx: '{}'",
338 component.id
339 )
340 })
341 }
342
343 fn push_sub_component(&mut self, sc: LoweredSubComponent) -> SubComponentIdx {
344 self.sub_components.push_and_get_key(sc)
345 }
346}
347
348fn component_id(component: &Rc<Component>) -> SmolStr {
349 if component.is_global() {
350 component.root_element.borrow().id.clone()
351 } else if component.from_library.get() {
352 component.id.clone()
353 } else if component.id.is_empty() {
354 format_smolstr!("Component_{}", component.root_element.borrow().id)
355 } else {
356 format_smolstr!("{}_{}", component.id, component.root_element.borrow().id)
357 }
358}
359
360fn lower_sub_component(
361 component: &Rc<Component>,
362 state: &mut LoweringState,
363 parent_context: Option<&ExpressionLoweringCtxInner>,
364 compiler_config: &CompilerConfiguration,
365) -> LoweredSubComponent {
366 let mut sub_component = SubComponent {
367 name: component_id(component),
368 properties: Default::default(),
369 callbacks: Default::default(),
370 functions: Default::default(),
371 items: Default::default(),
372 repeated: Default::default(),
373 component_containers: Default::default(),
374 popup_windows: Default::default(),
375 menu_item_trees: Vec::new(),
376 timers: Default::default(),
377 sub_components: Default::default(),
378 property_init: Default::default(),
379 change_callbacks: Default::default(),
380 animations: Default::default(),
381 two_way_bindings: Default::default(),
382 const_properties: Default::default(),
383 pre_init_code: Default::default(),
384 init_code: Default::default(),
385 geometries: Default::default(),
386 layout_info_h: super::Expression::BoolLiteral(false).into(),
388 layout_info_v: super::Expression::BoolLiteral(false).into(),
389 child_of_layout: component.root_element.borrow().child_of_layout,
390 grid_layout_input_for_repeated: None,
391 flexbox_layout_item_info_for_repeated: None,
392 cross_axis_self_alignment_for_repeated: None,
393 layout_order_for_repeated: None,
394 layout_info_v_constrained_for_repeated: None,
395 layout_info_v_at_cross_width_for_repeated: None,
396 grid_row_child_cross_width: None,
397 is_repeated_row: component
398 .root_element
399 .borrow()
400 .grid_layout_cell
401 .as_ref()
402 .is_some_and(|c| c.borrow().child_items.is_some()),
403 grid_layout_children: Default::default(),
404 row_child_templates: None,
405 accessible_prop: Default::default(),
406 element_infos: Default::default(),
407 prop_analysis: Default::default(),
408 debug_info: compiler_config.debug_info.then(|| super::debug_info::SubComponentDebugInfo {
409 source_location: crate::diagnostics::Spanned::to_source_location(
410 &*component.root_element.borrow(),
411 ),
412 items: Default::default(),
413 sub_component_use_sites: Default::default(),
414 }),
415 };
416 let mut mapping = LoweredSubComponentMapping::default();
417 let mut repeated = TiVec::new();
418 let mut accessible_prop = Vec::new();
419 let mut change_callbacks = Vec::new();
420
421 if let Some(parent) = component.parent_element() {
422 if parent.borrow().repeated.as_ref().is_some_and(|x| !x.is_conditional_element) {
424 sub_component.properties.push(Property {
425 name: "model_data".into(),
426 ty: crate::expression_tree::Expression::RepeaterModelReference {
427 element: component.parent_element.borrow().clone(),
428 }
429 .ty(),
430 ..Property::default()
431 });
432 sub_component.properties.push(Property {
433 name: "model_index".into(),
434 ty: Type::Int32,
435 ..Property::default()
436 });
437 }
438 };
439
440 let s: Option<ElementRc> = None;
441 let mut repeater_offset = 0;
442 crate::object_tree::recurse_elem(&component.root_element, &s, &mut |element, parent| {
443 let elem = element.borrow();
444 for (p, x) in &elem.property_declarations {
445 if x.is_alias.is_some() {
446 continue;
447 }
448 let reference = if let Type::Function(function) = &x.property_type {
449 let index = sub_component.functions.push_and_get_key(Function {
452 name: p.clone(),
453 ret_ty: function.return_type.clone(),
454 args: function.args.clone(),
455 code: super::Expression::CodeBlock(Vec::new()).into(),
457 use_count: Default::default(),
458 });
459 index.into()
460 } else if let Type::Callback(callback) = &x.property_type {
461 let index = sub_component.callbacks.push_and_get_key(Callback {
462 name: format_smolstr!("{}_{}", elem.id, p),
463 ret_ty: callback.return_type.clone(),
464 args: callback.args.clone(),
465 ty: Type::Callback(callback.clone()),
466 use_count: 0.into(),
467 needs_tracker: x.expose_in_public_api,
468 });
469 index.into()
470 } else {
471 let index = sub_component.properties.push_and_get_key(Property {
472 name: format_smolstr!("{}_{}", elem.id, p),
473 ty: x.property_type.clone(),
474 ..Property::default()
475 });
476 index.into()
477 };
478 mapping.property_mapping.insert(
479 NamedReference::new(element, p.clone()),
480 MemberReference::Relative {
481 parent_level: 0,
482 local_reference: LocalMemberReference {
483 sub_component_path: Vec::new(),
484 reference,
485 },
486 },
487 );
488 }
489 if elem.repeated.is_some() {
490 let parent = if elem.is_component_placeholder { parent.clone() } else { None };
491
492 mapping.element_mapping.insert(
493 element.clone().into(),
494 LoweredElement::Repeated {
495 repeated_index: repeated.push_and_get_key((element.clone(), parent)),
496 },
497 );
498 mapping.repeater_count += 1;
499 return None;
500 }
501 match &elem.base_type {
502 ElementType::Component(comp) => {
503 let ty = state.sub_component_idx(comp);
504 let sub_component_index =
505 sub_component.sub_components.push_and_get_key(SubComponentInstance {
506 ty,
507 name: elem.id.clone(),
508 index_in_tree: *elem.item_index.get().unwrap(),
509 index_of_first_child_in_tree: *elem
510 .item_index_of_first_children
511 .get()
512 .unwrap(),
513 repeater_offset,
514 });
515 if let Some(debug_info) = sub_component.debug_info.as_mut() {
516 let added_index = debug_info
517 .sub_component_use_sites
518 .push_and_get_key(crate::diagnostics::Spanned::to_source_location(&*elem));
519 debug_assert_eq!(added_index, sub_component_index);
520 }
521 mapping.element_mapping.insert(
522 element.clone().into(),
523 LoweredElement::SubComponent { sub_component_index },
524 );
525 repeater_offset += comp.repeater_count();
526 }
527
528 ElementType::Native(n) => {
529 let item_index = sub_component.items.push_and_get_key(Item {
530 ty: n.clone(),
531 name: elem.id.clone(),
532 index_in_tree: *elem.item_index.get().unwrap(),
533 });
534 if let Some(debug_info) = sub_component.debug_info.as_mut() {
535 let primary = elem.debug.first();
536 let source_location = crate::diagnostics::Spanned::to_source_location(&*elem);
537 let added_index =
538 debug_info.items.push_and_get_key(super::debug_info::ItemDebugInfo {
539 source_location,
540 qualified_id: primary.and_then(|d| d.qualified_id.clone()),
541 element_hash: primary.map(|d| d.element_hash).unwrap_or_default(),
542 is_injected_wrapper_element: elem.is_injected_wrapper_element,
543 });
544 debug_assert_eq!(added_index, item_index);
545 }
546 mapping
547 .element_mapping
548 .insert(element.clone().into(), LoweredElement::NativeItem { item_index });
549 }
550 _ => unreachable!(),
551 };
552 for (key, nr) in &elem.accessibility_props.0 {
553 let enum_value =
555 crate::generator::to_pascal_case(key.strip_prefix("accessible-").unwrap());
556 accessible_prop.push((*elem.item_index.get().unwrap(), enum_value, nr.clone()));
557 }
558
559 for (prop, expr) in &elem.change_callbacks {
560 change_callbacks
561 .push((NamedReference::new(element, prop.clone()), expr.borrow().clone()));
562 }
563
564 if compiler_config.debug_info {
565 let element_infos = elem.element_infos();
566 if !element_infos.is_empty() {
567 sub_component.element_infos.insert(*elem.item_index.get().unwrap(), element_infos);
568 }
569 }
570
571 Some(element.clone())
572 });
573
574 let inner = ExpressionLoweringCtxInner { mapping: &mapping, parent: parent_context, component };
575 let mut ctx = ExpressionLoweringCtx { inner, state };
576
577 sub_component.repeated = repeated
580 .into_iter()
581 .map(|(elem, parent)| {
582 lower_repeated_component(&elem, parent, &sub_component, &mut ctx, compiler_config)
583 })
584 .collect();
585 for s in &mut sub_component.sub_components {
586 s.repeater_offset +=
587 (sub_component.repeated.len() + sub_component.component_containers.len()) as u32;
588 }
589
590 crate::generator::handle_property_bindings_init(component, |e, p, binding| {
591 let nr = NamedReference::new(e, p.clone());
592 let prop = ctx.map_property_reference(&nr);
593
594 if let Type::Function { .. } = nr.ty() {
595 let MemberReference::Relative { parent_level, local_reference } = prop else {
596 unreachable!()
597 };
598 assert!(parent_level == 0);
599 assert!(local_reference.sub_component_path.is_empty());
600 let LocalMemberIndex::Function(function_index) = local_reference.reference else {
601 unreachable!()
602 };
603
604 sub_component.functions[function_index]
605 .code
606 .replace(super::lower_expression::lower_expression(&binding.expression, &mut ctx));
607
608 return;
609 }
610
611 for tw in &binding.two_way_bindings {
612 sub_component.two_way_bindings.push(match tw {
613 crate::expression_tree::TwoWayBinding::Property { property, field_access } => {
614 TwoWayBinding {
615 prop1: prop.local(),
616 prop2: ctx.map_property_reference(property),
617 field_access: field_access.clone(),
618 is_model: None,
619 }
620 }
621 crate::expression_tree::TwoWayBinding::ModelData {
622 repeated_element,
623 field_access,
624 } => TwoWayBinding {
625 prop1: prop.local(),
626 prop2: super::lower_expression::repeater_special_property(
627 repeated_element,
628 component,
629 PropertyIdx::REPEATER_DATA,
630 ),
631 field_access: field_access.clone(),
632 is_model: Some(PropertyIdx::REPEATER_INDEX),
633 },
634 });
635 }
636 if !matches!(binding.expression, tree_Expression::Invalid) {
637 let expression =
638 super::lower_expression::lower_expression(&binding.expression, &mut ctx).into();
639
640 let is_constant = binding.analysis.as_ref().is_some_and(|a| a.is_const);
641 let animation = binding
642 .animation
643 .as_ref()
644 .filter(|_| !is_constant)
645 .map(|a| super::lower_expression::lower_animation(a, &mut ctx));
646
647 sub_component.prop_analysis.insert(
648 prop.clone(),
649 PropAnalysis {
650 property_init: Some(sub_component.property_init.len()),
651 analysis: get_property_analysis(e, p),
652 },
653 );
654
655 let kind = if matches!(
656 e.borrow().lookup_property(p, PropertyLookupMode::InternalName).property_type,
657 Type::Struct(s) if matches!(s.name, StructName::Builtin(BuiltinStruct::StateInfo))
658 ) {
659 BindingKind::State
660 } else if is_constant {
661 BindingKind::Constant
662 } else {
663 BindingKind::Normal
664 };
665
666 sub_component.property_init.push((
667 prop.clone(),
668 BindingExpression { expression, animation, kind, use_count: 0.into() },
669 ));
670 }
671
672 if e.borrow()
673 .property_analysis
674 .borrow()
675 .get(p)
676 .is_none_or(|a| a.is_set || a.is_set_externally)
677 && let Some(anim) = binding.animation.as_ref()
678 {
679 match super::lower_expression::lower_animation(anim, &mut ctx) {
680 Animation::Static(anim) => {
681 sub_component.animations.insert(prop.local(), anim);
682 }
683 Animation::Transition(_) => {
684 }
686 }
687 }
688 });
689
690 sub_component.popup_windows = component
691 .popup_windows
692 .borrow()
693 .iter()
694 .map(|popup| lower_popup_component(popup, &mut ctx, compiler_config))
695 .collect();
696
697 sub_component.menu_item_trees = component
698 .menu_item_tree
699 .borrow()
700 .iter()
701 .map(|c| {
702 let sc = lower_sub_component(c, ctx.state, Some(&ctx.inner), compiler_config);
703 ItemTree {
704 tree: make_tree(ctx.state, &c.root_element, &sc, &[]),
705 root: ctx.state.push_sub_component(sc),
706 }
707 })
708 .collect();
709
710 sub_component.timers = component.timers.borrow().iter().map(|t| lower_timer(t, &ctx)).collect();
711
712 crate::generator::for_each_const_properties(component, |elem, n| {
713 let x = ctx.map_property_reference(&NamedReference::new(elem, n.clone()));
714 sub_component.prop_analysis.entry(x.clone()).or_insert_with(|| PropAnalysis {
716 property_init: None,
717 analysis: get_property_analysis(elem, n),
718 });
719 sub_component.const_properties.push(x.local());
720 });
721
722 sub_component.pre_init_code = component
723 .init_code
724 .borrow()
725 .font_registration_code
726 .iter()
727 .map(|e| super::lower_expression::lower_expression(e, &mut ctx).into())
728 .collect();
729
730 sub_component.init_code = component
731 .init_code
732 .borrow()
733 .iter_without_font_registration()
734 .map(|e| super::lower_expression::lower_expression(e, &mut ctx).into())
735 .collect();
736
737 sub_component.layout_info_h = super::lower_layout_expression::get_layout_info(
738 &component.root_element,
739 &mut ctx,
740 &component.root_constraints.borrow(),
741 crate::layout::Orientation::Horizontal,
742 None,
743 )
744 .into();
745 let v_cross_constraint = component
748 .root_element
749 .borrow()
750 .layout_info_v_with_constraint
751 .is_some()
752 .then(|| {
753 super::lower_layout_expression::default_cross_axis_constraint(&component.root_element)
754 })
755 .flatten();
756 sub_component.layout_info_v = super::lower_layout_expression::get_layout_info(
757 &component.root_element,
758 &mut ctx,
759 &component.root_constraints.borrow(),
760 crate::layout::Orientation::Vertical,
761 v_cross_constraint,
762 )
763 .into();
764 if component.root_element.borrow().child_of_flexbox {
765 let root_elem = &component.root_element;
766 let has_flex_binding = ["cross-axis-self-alignment", "layout-order"]
767 .iter()
768 .any(|name| crate::layout::binding_reference(root_elem, name).is_some());
769 let v_constrained =
770 super::lower_layout_expression::get_layout_info_v_constrained_for_repeated(
771 &mut ctx,
772 root_elem,
773 &component.root_constraints.borrow(),
774 );
775 if has_flex_binding || v_constrained.is_some() {
779 sub_component.flexbox_layout_item_info_for_repeated = Some(
780 super::lower_layout_expression::get_flexbox_layout_item_info_for_repeated(
781 &mut ctx, root_elem,
782 )
783 .into(),
784 );
785 }
786 sub_component.layout_info_v_constrained_for_repeated = v_constrained.map(Into::into);
787 sub_component.layout_info_v_at_cross_width_for_repeated =
788 super::lower_layout_expression::get_layout_info_v_at_cross_width_for_repeated(
789 &mut ctx,
790 root_elem,
791 &component.root_constraints.borrow(),
792 true,
793 )
794 .map(Into::into);
795 } else if let Some(box_orientation) =
796 component.root_element.borrow().parent_box_layout_orientation
797 {
798 if let Some(nr) =
802 crate::layout::binding_reference(&component.root_element, "cross-axis-self-alignment")
803 {
804 sub_component.cross_axis_self_alignment_for_repeated = Some((
805 box_orientation.orthogonal(),
806 super::Expression::PropertyReference(ctx.map_property_reference(&nr)).into(),
807 ));
808 }
809 if let Some(nr) = crate::layout::binding_reference(&component.root_element, "layout-order")
810 {
811 sub_component.layout_order_for_repeated = Some((
812 box_orientation,
813 super::Expression::PropertyReference(ctx.map_property_reference(&nr)).into(),
814 ));
815 }
816 sub_component.layout_info_v_at_cross_width_for_repeated =
822 super::lower_layout_expression::get_layout_info_v_at_cross_width_for_repeated(
823 &mut ctx,
824 &component.root_element,
825 &component.root_constraints.borrow(),
826 false,
827 )
828 .map(Into::into);
829 }
830
831 if component.root_element.borrow().grid_layout_cell.is_some() {
832 sub_component.layout_info_v_at_cross_width_for_repeated =
836 super::lower_layout_expression::get_layout_info_v_at_cross_width_for_repeated(
837 &mut ctx,
838 &component.root_element,
839 &component.root_constraints.borrow(),
840 false,
841 )
842 .map(Into::into);
843 }
844
845 if let Some(grid_layout_cell) = component.root_element.borrow().grid_layout_cell.as_ref() {
846 let grid_cell_ref = grid_layout_cell.borrow();
847 sub_component.grid_layout_input_for_repeated = Some(
848 super::lower_layout_expression::get_grid_layout_input_for_repeated(
849 &mut ctx,
850 &grid_cell_ref,
851 )
852 .into(),
853 );
854
855 if let Some(children_constraints) = grid_cell_ref.child_items.as_ref() {
857 let mut row_child_templates = Vec::new();
858 for child_template in children_constraints.iter() {
859 match child_template {
860 crate::layout::RowChildTemplate::Static(layout_item) => {
861 let layout_info_h = super::lower_layout_expression::get_layout_info(
862 &layout_item.element,
863 &mut ctx,
864 &layout_item.constraints,
865 crate::layout::Orientation::Horizontal,
866 None,
867 );
868 let layout_info_v = super::lower_layout_expression::get_layout_info(
869 &layout_item.element,
870 &mut ctx,
871 &layout_item.constraints,
872 crate::layout::Orientation::Vertical,
873 None,
874 );
875 let child_index = sub_component.grid_layout_children.push_and_get_key(
876 super::GridLayoutChildLayoutInfo {
877 layout_info_h: layout_info_h.into(),
878 layout_info_v: layout_info_v.into(),
879 },
880 );
881 row_child_templates
882 .push(super::RowChildTemplateInfo::Static { child_index });
883 }
884 crate::layout::RowChildTemplate::Repeated { repeated_element, .. } => {
885 let cross_width = super::lower_layout_expression::grid_measure_cross_width(
890 &mut ctx,
891 repeated_element,
892 super::lower_layout_expression::GridMeasureIndex::RowChild,
893 );
894 let measure_at_cross_width = cross_width.is_some();
895 if sub_component.grid_row_child_cross_width.is_none() {
896 sub_component.grid_row_child_cross_width = cross_width.map(Into::into);
897 }
898 if let Some(super::lower_to_item_tree::LoweredElement::Repeated {
900 repeated_index,
901 }) = mapping.element_mapping.get(&repeated_element.clone().into())
902 {
903 row_child_templates.push(super::RowChildTemplateInfo::Repeated {
904 repeater_index: *repeated_index,
905 measure_at_cross_width,
906 });
907 }
908 }
909 }
910 }
911 sub_component.row_child_templates = Some(row_child_templates);
915 }
916 }
917
918 sub_component.accessible_prop = accessible_prop
919 .into_iter()
920 .map(|(idx, key, nr)| {
921 let prop = ctx.map_property_reference(&nr);
922 let expr = match nr.ty() {
923 Type::Bool => super::Expression::Condition {
924 condition: super::Expression::PropertyReference(prop).into(),
925 true_expr: super::Expression::StringLiteral("true".into()).into(),
926 false_expr: super::Expression::StringLiteral("false".into()).into(),
927 },
928 Type::Int32 | Type::Float32 => super::Expression::Cast {
929 from: super::Expression::PropertyReference(prop).into(),
930 to: Type::String,
931 },
932 Type::String => super::Expression::PropertyReference(prop),
933 Type::Enumeration(ref e) if e.name == "AccessibleRole" => {
934 super::Expression::PropertyReference(prop)
935 }
936 Type::Enumeration(_) => super::Expression::Cast {
937 from: super::Expression::PropertyReference(prop).into(),
938 to: Type::String,
939 },
940 Type::Callback(callback) => super::Expression::CallBackCall {
941 callback: prop,
942 arguments: (0..callback.args.len())
943 .map(|index| super::Expression::FunctionParameterReference { index })
944 .collect(),
945 },
946 _ => panic!("Invalid type for accessible property"),
947 };
948
949 ((idx, key), expr.into())
950 })
951 .collect();
952
953 sub_component.change_callbacks = change_callbacks
954 .into_iter()
955 .map(|(nr, exprs)| {
956 let prop = ctx.map_property_reference(&nr);
957 let expr = super::lower_expression::lower_expression(
958 &tree_Expression::CodeBlock(exprs),
959 &mut ctx,
960 );
961 (prop, expr.into())
962 })
963 .collect();
964
965 crate::object_tree::recurse_elem(&component.root_element, &(), &mut |element, _| {
966 let elem = element.borrow();
967 if elem.repeated.is_some() {
968 return;
969 };
970 let Some(geom) = &elem.geometry_props else { return };
971 let item_index = *elem.item_index.get().unwrap() as usize;
972 if item_index >= sub_component.geometries.len() {
973 sub_component.geometries.resize(item_index + 1, Default::default());
974 }
975 sub_component.geometries[item_index] = Some(lower_geometry(geom, &ctx).into());
976 });
977
978 LoweredSubComponent { sub_component, mapping }
979}
980
981fn lower_geometry(
982 geom: &crate::object_tree::GeometryProps,
983 ctx: &ExpressionLoweringCtx<'_>,
984) -> super::Expression {
985 let mut fields = BTreeMap::default();
986 let mut values = BTreeMap::default();
987 for (f, v) in [("x", &geom.x), ("y", &geom.y), ("width", &geom.width), ("height", &geom.height)]
988 {
989 fields.insert(f.into(), Type::LogicalLength);
990 values
991 .insert(f.into(), super::Expression::PropertyReference(ctx.map_property_reference(v)));
992 }
993 super::Expression::Struct { ty: Arc::new(Struct::new(fields, StructName::None)), values }
994}
995
996fn get_property_analysis(elem: &ElementRc, p: &str) -> crate::object_tree::PropertyAnalysis {
997 let mut a = elem.borrow().property_analysis.borrow().get(p).cloned().unwrap_or_default();
998 let mut elem = elem.clone();
999 loop {
1000 if let Some(d) = elem.borrow().property_declarations.get(p) {
1001 if let Some(nr) = &d.is_alias {
1002 a.merge(&get_property_analysis(&nr.element(), nr.name()));
1003 }
1004 return a;
1005 }
1006 let base = elem.borrow().base_type.clone();
1007 match base {
1008 ElementType::Native(n) if n.properties.get(p).is_some_and(|p| p.is_native_output()) => {
1009 a.is_set = true;
1010 }
1011 ElementType::Component(c) => {
1012 elem = c.root_element.clone();
1013 if let Some(a2) = elem.borrow().property_analysis.borrow().get(p) {
1014 a.merge_with_base(a2);
1015 }
1016 continue;
1017 }
1018 _ => (),
1019 };
1020 return a;
1021 }
1022}
1023
1024fn lower_repeated_component(
1025 elem: &ElementRc,
1026 parent_component_container: Option<ElementRc>,
1027 sub_component: &SubComponent,
1028 ctx: &mut ExpressionLoweringCtx,
1029 compiler_config: &CompilerConfiguration,
1030) -> RepeatedElement {
1031 let e = elem.borrow();
1032 let component = e.base_type.as_component().clone();
1033 let repeated = e.repeated.as_ref().unwrap();
1034
1035 let sc = lower_sub_component(&component, ctx.state, Some(&ctx.inner), compiler_config);
1036
1037 let listview = repeated.is_listview.as_ref().map(|lv| {
1038 let geom = component.root_element.borrow().geometry_props.clone().unwrap();
1039 ListViewInfo {
1040 content_y: ctx.map_property_reference(&lv.content_y),
1041 content_height: lv
1042 .content_height
1043 .as_ref()
1044 .map(|content_height| ctx.map_property_reference(content_height)),
1045 content_width: lv
1046 .content_width
1047 .as_ref()
1048 .map(|content_width| ctx.map_property_reference(content_width)),
1049 listview_height: ctx.map_property_reference(&lv.listview_height),
1050 listview_width: ctx.map_property_reference(&lv.listview_width),
1051 prop_y: sc.mapping.map_property_reference(&geom.y, ctx.state),
1052 prop_height: sc.mapping.map_property_reference(&geom.height, ctx.state),
1053 }
1054 });
1055
1056 let parent_index = parent_component_container.map(|p| *p.borrow().item_index.get().unwrap());
1057 let container_item_index =
1058 parent_index.and_then(|pii| sub_component.items.position(|i| i.index_in_tree == pii));
1059
1060 let dynamic_z = match &e.z_order {
1061 Some(object_tree::ZOrder::PerInstance(nr)) => {
1062 Some(sc.mapping.map_property_reference(nr, ctx.state))
1063 }
1064 _ => None,
1065 };
1066
1067 let tree = make_tree(ctx.state, &component.root_element, &sc, &[]);
1068 let root = ctx.state.push_sub_component(sc);
1069 ctx.state.sub_component_mapping.insert(ByAddress(component.clone()), root);
1071
1072 RepeatedElement {
1073 model: super::lower_expression::lower_expression(&repeated.model, ctx).into(),
1074 sub_tree: ItemTree { tree, root },
1075 index_prop: (!repeated.is_conditional_element).then_some(PropertyIdx::REPEATER_INDEX),
1076 data_prop: (!repeated.is_conditional_element).then_some(PropertyIdx::REPEATER_DATA),
1077 dynamic_z,
1078 index_in_tree: *e.item_index.get().unwrap(),
1079 listview,
1080 container_item_index,
1081 }
1082}
1083
1084fn lower_popup_component(
1085 popup: &object_tree::PopupWindow,
1086 ctx: &mut ExpressionLoweringCtx,
1087 compiler_config: &CompilerConfiguration,
1088) -> PopupWindow {
1089 let sc = lower_sub_component(&popup.component, ctx.state, Some(&ctx.inner), compiler_config);
1090 use super::Expression::PropertyReference as PR;
1091 let position = super::lower_expression::make_struct(
1092 BuiltinStruct::LogicalPosition,
1093 [
1094 ("x", Type::LogicalLength, PR(sc.mapping.map_property_reference(&popup.x, ctx.state))),
1095 ("y", Type::LogicalLength, PR(sc.mapping.map_property_reference(&popup.y, ctx.state))),
1096 ],
1097 );
1098
1099 let item_tree = ItemTree {
1100 tree: make_tree(ctx.state, &popup.component.root_element, &sc, &[]),
1101 root: ctx.state.push_sub_component(sc),
1102 };
1103 PopupWindow { item_tree, position: position.into(), is_tooltip: popup.is_tooltip }
1104}
1105
1106fn lower_timer(timer: &object_tree::Timer, ctx: &ExpressionLoweringCtx) -> Timer {
1107 Timer {
1108 interval: super::Expression::PropertyReference(ctx.map_property_reference(&timer.interval))
1109 .into(),
1110 running: super::Expression::PropertyReference(ctx.map_property_reference(&timer.running))
1111 .into(),
1112 triggered: super::Expression::CallBackCall {
1114 callback: ctx.map_property_reference(&timer.triggered),
1115 arguments: Vec::new(),
1116 }
1117 .into(),
1118 }
1119}
1120
1121fn lower_global(
1123 global: &Rc<Component>,
1124 global_index: GlobalIdx,
1125 state: &mut LoweringState,
1126) -> GlobalComponent {
1127 let mut properties = TiVec::new();
1128 let mut callbacks = TiVec::new();
1129 let mut const_properties = TiVec::new();
1130 let mut prop_analysis = TiVec::new();
1131 let mut functions = TiVec::new();
1132
1133 for (p, x) in &global.root_element.borrow().property_declarations {
1134 if x.is_alias.is_some() {
1135 continue;
1136 }
1137 let nr = NamedReference::new(&global.root_element, p.clone());
1138
1139 if let Type::Function(function) = &x.property_type {
1140 let function_index: FunctionIdx = functions.push_and_get_key(Function {
1142 name: p.clone(),
1143 ret_ty: function.return_type.clone(),
1144 args: function.args.clone(),
1145 code: super::Expression::CodeBlock(Vec::new()).into(),
1147 use_count: Default::default(),
1148 });
1149 state.global_properties.insert(
1150 nr.clone(),
1151 MemberReference::Global { global_index, member: function_index.into() },
1152 );
1153 continue;
1154 } else if let Type::Callback(cb) = &x.property_type {
1155 let callback_index: CallbackIdx = callbacks.push_and_get_key(Callback {
1156 name: p.clone(),
1157 ret_ty: cb.return_type.clone(),
1158 args: cb.args.clone(),
1159 ty: x.property_type.clone(),
1160 use_count: 0.into(),
1161 needs_tracker: x.expose_in_public_api,
1162 });
1163 state.global_properties.insert(
1164 nr.clone(),
1165 MemberReference::Global { global_index, member: callback_index.into() },
1166 );
1167 continue;
1168 }
1169
1170 let property_index: PropertyIdx = properties.push_and_get_key(Property {
1171 name: p.clone(),
1172 ty: x.property_type.clone(),
1173 ..Property::default()
1174 });
1175
1176 const_properties.push(nr.is_constant());
1177
1178 prop_analysis.push(
1179 global
1180 .root_element
1181 .borrow()
1182 .property_analysis
1183 .borrow()
1184 .get(p)
1185 .cloned()
1186 .unwrap_or_default(),
1187 );
1188 state.global_properties.insert(
1189 nr.clone(),
1190 MemberReference::Global { global_index, member: property_index.into() },
1191 );
1192 }
1193
1194 let is_builtin = if let Some(builtin) = global.root_element.borrow().native_class() {
1195 for (p, x) in &builtin.properties {
1197 let property_index = properties.push_and_get_key(Property {
1198 name: p.clone(),
1199 ty: x.ty.clone(),
1200 ..Property::default()
1201 });
1202 let nr = NamedReference::new(&global.root_element, p.clone());
1203 state.global_properties.insert(
1204 nr,
1205 MemberReference::Global { global_index, member: property_index.into() },
1206 );
1207 prop_analysis.push(PropertyAnalysis {
1208 is_set_externally: true,
1210 ..global
1211 .root_element
1212 .borrow()
1213 .property_analysis
1214 .borrow()
1215 .get(p)
1216 .cloned()
1217 .unwrap_or_default()
1218 });
1219 }
1220 true
1221 } else {
1222 false
1223 };
1224
1225 GlobalComponent {
1226 name: global.root_element.borrow().id.clone(),
1227 init_values: BTreeMap::new(),
1228 properties,
1229 callbacks,
1230 functions,
1231 change_callbacks: BTreeMap::new(),
1232 const_properties,
1233 public_properties: Default::default(),
1234 private_properties: global.private_properties.borrow().clone(),
1235 exported: !global.exported_global_names.borrow().is_empty(),
1236 aliases: global.global_aliases(),
1237 is_builtin,
1238 from_library: global.from_library.get(),
1239 prop_analysis,
1240 }
1241}
1242
1243fn lower_global_expressions(
1244 global: &Rc<Component>,
1245 state: &mut LoweringState,
1246 lowered: &mut GlobalComponent,
1247) {
1248 let mapping = LoweredSubComponentMapping::default();
1250 let inner = ExpressionLoweringCtxInner { mapping: &mapping, parent: None, component: global };
1251 let mut ctx = ExpressionLoweringCtx { inner, state };
1252
1253 for (prop, binding) in global.root_element.borrow().bindings_including_synthetic() {
1254 assert!(binding.borrow().two_way_bindings.is_empty());
1255 assert!(binding.borrow().animation.is_none());
1256 let expression =
1257 super::lower_expression::lower_expression(&binding.borrow().expression, &mut ctx);
1258
1259 let nr = NamedReference::new(&global.root_element, prop.clone());
1260 let member_index = match &ctx.state.global_properties[&nr] {
1261 MemberReference::Global {
1262 member: LocalMemberIndex::Function(function_index), ..
1263 } => {
1264 lowered.functions[*function_index].code.replace(expression);
1265 continue;
1266 }
1267 MemberReference::Global { member, .. } => member.clone(),
1268 _ => unreachable!(),
1269 };
1270 let is_constant = binding.borrow().analysis.as_ref().is_some_and(|a| a.is_const);
1271 lowered.init_values.insert(
1272 member_index,
1273 BindingExpression {
1274 expression: expression.into(),
1275 animation: None,
1276 kind: if is_constant { BindingKind::Constant } else { BindingKind::Normal },
1277 use_count: 0.into(),
1278 },
1279 );
1280 }
1281
1282 for (prop, expr) in &global.root_element.borrow().change_callbacks {
1283 let nr = NamedReference::new(&global.root_element, prop.clone());
1284 let MemberReference::Global { member: LocalMemberIndex::Property(property_index), .. } =
1285 ctx.state.global_properties[&nr]
1286 else {
1287 unreachable!()
1288 };
1289 let expression = super::lower_expression::lower_expression(
1290 &tree_Expression::CodeBlock(expr.borrow().clone()),
1291 &mut ctx,
1292 );
1293 lowered.change_callbacks.insert(property_index, expression.into());
1294 }
1295
1296 if let Some(builtin) = global.root_element.borrow().native_class() {
1297 if lowered.exported {
1298 lowered.public_properties = builtin
1299 .properties
1300 .iter()
1301 .map(|(p, c)| {
1302 let property_reference = mapping.map_property_reference(
1303 &NamedReference::new(&global.root_element, p.clone()),
1304 state,
1305 );
1306 (
1307 p.clone(),
1308 PublicProperty {
1309 display_name: p.clone(),
1310 ty: c.ty.clone(),
1311 prop: property_reference,
1312 visibility: c.property_visibility,
1313 },
1314 )
1315 })
1316 .collect()
1317 }
1318 } else {
1319 lowered.public_properties = public_properties(global, &mapping, state);
1320 }
1321}
1322
1323fn make_tree(
1324 state: &LoweringState,
1325 element: &ElementRc,
1326 component: &LoweredSubComponent,
1327 sub_component_path: &[SubComponentInstanceIdx],
1328) -> TreeNode {
1329 let e = element.borrow();
1330 let children = e.children.iter().map(|c| make_tree(state, c, component, sub_component_path));
1331 let repeater_count = component.mapping.repeater_count;
1332
1333 let zero = || ZSource::Expression(super::Expression::NumberLiteral(0.).into());
1334 let z_sort_order_property = if e.has_dynamic_z_order() {
1335 use crate::object_tree::ZOrder;
1336 let mut z_sources: Vec<ZSource> = Vec::with_capacity(e.children.len());
1337 for child in e.children.iter() {
1338 let child_z = child.borrow().z_order.clone();
1339 match child_z {
1340 Some(ZOrder::Constant(val)) => {
1341 z_sources.push(ZSource::Expression(
1342 super::Expression::NumberLiteral(val as f64).into(),
1343 ));
1344 }
1345 Some(ZOrder::PerInstance(_)) => z_sources.push(ZSource::RepeaterInstances),
1346 Some(ZOrder::Dynamic(ref nr)) => {
1347 match component.mapping.map_property_reference(nr, state) {
1348 MemberReference::Relative { parent_level, local_reference } => {
1349 debug_assert_eq!(
1351 parent_level, 0,
1352 "z reference resolved outside the item tree"
1353 );
1354 let mut full_path = sub_component_path.to_vec();
1357 full_path.extend_from_slice(&local_reference.sub_component_path);
1358 z_sources.push(ZSource::Expression(
1359 super::Expression::PropertyReference(
1360 LocalMemberReference {
1361 sub_component_path: full_path,
1362 reference: local_reference.reference,
1363 }
1364 .into(),
1365 )
1366 .into(),
1367 ));
1368 }
1369 global @ MemberReference::Global { .. } => {
1370 z_sources.push(ZSource::Expression(
1371 super::Expression::PropertyReference(global).into(),
1372 ));
1373 }
1374 }
1375 }
1376 None => z_sources.push(zero()),
1377 }
1378 }
1379 Some(z_sources)
1380 } else {
1381 None
1382 };
1383
1384 match component.mapping.element_mapping.get(&ByAddress(element.clone())).unwrap() {
1385 LoweredElement::SubComponent { sub_component_index } => {
1386 let sub_component = e.sub_component().unwrap();
1387 let new_sub_component_path = sub_component_path
1388 .iter()
1389 .copied()
1390 .chain(std::iter::once(*sub_component_index))
1391 .collect::<Vec<_>>();
1392 let mut tree_node = make_tree(
1393 state,
1394 &sub_component.root_element,
1395 state.sub_component(sub_component),
1396 &new_sub_component_path,
1397 );
1398 let inner_count = tree_node.children.len();
1401 tree_node.children.extend(children);
1402 if z_sort_order_property.is_some() || tree_node.z_sort_order_property.is_some() {
1403 let mut merged = tree_node
1404 .z_sort_order_property
1405 .take()
1406 .unwrap_or_else(|| (0..inner_count).map(|_| zero()).collect());
1407 let outer_count = tree_node.children.len() - inner_count;
1408 merged.extend(
1409 z_sort_order_property
1410 .unwrap_or_else(|| (0..outer_count).map(|_| zero()).collect()),
1411 );
1412 tree_node.z_sort_order_property = Some(merged);
1413 }
1414 tree_node.is_accessible |= !e.accessibility_props.0.is_empty();
1415 tree_node
1416 }
1417 LoweredElement::NativeItem { item_index } => TreeNode {
1418 is_accessible: !e.accessibility_props.0.is_empty(),
1419 sub_component_path: sub_component_path.into(),
1420 item_index: itertools::Either::Left(*item_index),
1421 children: children.collect(),
1422 z_sort_order_property,
1423 },
1424 LoweredElement::Repeated { repeated_index } => TreeNode {
1425 is_accessible: false,
1426 sub_component_path: sub_component_path.into(),
1427 item_index: itertools::Either::Right(usize::from(*repeated_index) as u32),
1428 children: Vec::new(),
1429 z_sort_order_property: None,
1430 },
1431 LoweredElement::ComponentPlaceholder { repeated_index } => TreeNode {
1432 is_accessible: false,
1433 sub_component_path: sub_component_path.into(),
1434 item_index: itertools::Either::Right(*repeated_index + repeater_count),
1435 children: Vec::new(),
1436 z_sort_order_property: None,
1437 },
1438 }
1439}
1440
1441fn public_properties(
1442 component: &Component,
1443 mapping: &LoweredSubComponentMapping,
1444 state: &LoweringState,
1445) -> PublicProperties {
1446 component
1447 .root_element
1448 .borrow()
1449 .property_declarations
1450 .iter()
1451 .filter(|(_, c)| c.expose_in_public_api)
1452 .map(|(p, c)| {
1453 let property_reference = mapping.map_property_reference(
1454 &NamedReference::new(&component.root_element, p.clone()),
1455 state,
1456 );
1457 let display_name = c
1461 .node
1462 .as_ref()
1463 .and_then(|n| {
1464 n.child_node(crate::parser::SyntaxKind::DeclaredIdentifier)
1465 .and_then(|n| n.child_token(crate::parser::SyntaxKind::Identifier))
1466 })
1467 .map(|tok| SmolStr::new(tok.text()))
1468 .unwrap_or_else(|| c.declared_name(p).clone());
1469 (
1470 c.declared_name(p).clone(),
1473 PublicProperty {
1474 display_name,
1475 ty: c.property_type.clone(),
1476 prop: property_reference,
1477 visibility: c.visibility,
1478 },
1479 )
1480 })
1481 .collect()
1482}