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