1#![allow(clippy::mutable_key_type)] use crate::diagnostics::{BuildDiagnostics, Spanned};
9use crate::expression_tree::{BindingExpression, Expression, NamedReference};
10use crate::langtype::{ElementType, PropertyLookupMode, Type};
11use crate::object_tree::*;
12use by_address::ByAddress;
13use smol_str::SmolStr;
14use std::cell::RefCell;
15use std::collections::btree_map::Entry;
16use std::collections::{HashMap, HashSet};
17use std::rc::Rc;
18
19#[derive(Copy, Clone, Eq, PartialEq)]
20pub enum InlineSelection {
21 InlineAllComponents,
22 InlineOnlyRequiredComponents,
23}
24
25pub fn inline(doc: &Document, inline_selection: InlineSelection, diag: &mut BuildDiagnostics) {
26 fn inline_components_recursively(
27 component: &Rc<Component>,
28 roots: &HashSet<ByAddress<Rc<Component>>>,
29 inline_selection: InlineSelection,
30 diag: &mut BuildDiagnostics,
31 ) {
32 recurse_elem_no_borrow(&component.root_element, &(), &mut |elem, _| {
33 let base = elem.borrow().base_type.clone();
34 if let ElementType::Component(c) = base {
35 inline_components_recursively(&c, roots, inline_selection, diag);
37
38 if c.parent_element().is_some() {
39 return;
41 }
42
43 if match inline_selection {
45 InlineSelection::InlineAllComponents => true,
46 InlineSelection::InlineOnlyRequiredComponents => {
47 component_requires_inlining(&c)
48 || element_require_inlining(elem)
49 || component.parent_element().is_none() && Rc::ptr_eq(elem, &component.root_element)
52 || roots.contains(&ByAddress(c.clone()))
54 }
55 } {
56 inline_element(elem, &c, component, diag);
57 }
58 }
59 });
60 component.popup_windows.borrow().iter().for_each(|p| {
61 inline_components_recursively(&p.component, roots, inline_selection, diag)
62 })
63 }
64 let mut roots = HashSet::new();
65 if inline_selection == InlineSelection::InlineOnlyRequiredComponents {
66 for component in doc.exported_roots().chain(doc.popup_menu_impl.iter().cloned()) {
67 roots.insert(ByAddress(component.clone()));
68 }
69 }
70 for component in doc.exported_roots().chain(doc.popup_menu_impl.iter().cloned()) {
71 inline_components_recursively(&component, &roots, inline_selection, diag);
72 let mut init_code = component.init_code.borrow_mut();
73 let inlined_init_code = core::mem::take(&mut init_code.inlined_init_code);
74 init_code.constructor_code.splice(0..0, inlined_init_code.into_values());
75 }
76}
77
78fn element_key(e: ElementRc) -> ByAddress<ElementRc> {
79 ByAddress(e)
80}
81
82type Mapping = HashMap<ByAddress<ElementRc>, ElementRc>;
83
84fn inline_element(
85 elem: &ElementRc,
86 inlined_component: &Rc<Component>,
87 root_component: &Rc<Component>,
88 diag: &mut BuildDiagnostics,
89) {
90 debug_assert_eq!(elem.borrow().base_type, ElementType::Component(inlined_component.clone()));
92 debug_assert!(
93 inlined_component.root_element.borrow().repeated.is_none(),
94 "root element of a component cannot be repeated"
95 );
96 debug_assert!(inlined_component.parent_element().is_none());
97
98 let mut elem_mut = elem.borrow_mut();
99 let priority_delta = 1 + elem_mut.inline_depth;
100 elem_mut.base_type = inlined_component.root_element.borrow().base_type.clone();
101 let Element { id: elem_id, property_declarations, .. } = &mut *elem_mut;
102 for (name, decl) in inlined_component.root_element.borrow().property_declarations.iter() {
103 match property_declarations.entry(name.clone()) {
104 Entry::Occupied(_) => debug_assert!(
107 crate::typeregister::reserved_member_function(name).is_some(),
108 "inlining {} into {} would merge two declarations of the same name",
109 inlined_component.id,
110 elem_id
111 ),
112 Entry::Vacant(e) => {
113 e.insert(PropertyDeclaration { expose_in_public_api: false, ..decl.clone() });
114 }
115 }
116 }
117 for (source_name, internal_name) in &inlined_component.root_element.borrow().shadowing_members {
119 elem_mut
120 .shadowing_members
121 .entry(source_name.clone())
122 .or_insert_with(|| internal_name.clone());
123 }
124
125 for (p, a) in inlined_component.root_element.borrow().property_analysis.borrow().iter() {
126 elem_mut.property_analysis.borrow_mut().entry(p.clone()).or_default().merge_with_base(a);
127 }
128
129 debug_assert!(inlined_component.root_element.borrow().states.is_empty());
131 debug_assert!(inlined_component.root_element.borrow().transitions.is_empty());
132
133 let mut mapping = HashMap::new();
135 mapping.insert(element_key(inlined_component.root_element.clone()), elem.clone());
136
137 let mut new_children = Vec::with_capacity(
138 elem_mut.children.len() + inlined_component.root_element.borrow().children.len(),
139 );
140 new_children.extend(
141 inlined_component.root_element.borrow().children.iter().map(|x| {
142 duplicate_element_with_mapping(x, &mut mapping, root_component, priority_delta)
143 }),
144 );
145
146 let inlined_insertion_points_orig = inlined_component.child_insertion_points.borrow();
148 let mut inlined_insertion_points = inlined_insertion_points_orig.clone();
149
150 if !inlined_insertion_points.contains_key(DEFAULT_SLOT_NAME)
155 && let Some(builtin) = inlined_component.root_element.borrow().builtin_type()
156 && !builtin.is_non_item_type
157 {
158 let cip_node = inlined_component
159 .node
160 .as_ref()
161 .map(|n| n.clone().into())
162 .or_else(|| {
163 inlined_component
164 .root_element
165 .borrow()
166 .debug
167 .first()
168 .map(|debug| debug.node.clone().into())
169 })
170 .or_else(|| elem_mut.debug.first().map(|debug| debug.node.clone().into()))
171 .or_else(|| root_component.node.as_ref().map(|n| n.clone().into()))
172 .expect("Missing syntax node for implicit @children insertion point");
173
174 inlined_insertion_points.insert(
175 DEFAULT_SLOT_NAME.into(),
176 ChildrenInsertionPoint {
177 parent: inlined_component.root_element.clone(),
178 insertion_index: inlined_component.root_element.borrow().children.len(),
179 node: ChildInsertionPointNode::DefaultChildrenPlaceHolder(cip_node),
180 },
181 );
182 }
183
184 let mut children_by_slot: HashMap<SmolStr, Vec<ElementRc>> = HashMap::new();
187 for child in std::mem::take(&mut elem_mut.children) {
188 let slot = child
189 .borrow()
190 .slot_target
191 .as_ref()
192 .map(|s| crate::parser::normalize_identifier(s))
193 .unwrap_or_else(|| DEFAULT_SLOT_NAME.into());
194
195 children_by_slot.entry(slot).or_default().push(child);
196 }
197
198 let mut unknown_slots = Vec::new();
200 for (slot_name, children) in &children_by_slot {
201 if slot_name == DEFAULT_SLOT_NAME {
202 continue;
205 }
206 if !inlined_insertion_points.contains_key(slot_name.as_str()) {
207 for child in children {
208 diag.push_error(
209 format!("Unknown slot '{slot_name}' in '{}'", inlined_component.id),
210 &*child.borrow(),
211 );
212 }
213 unknown_slots.push(slot_name.clone());
214 }
215 }
216 for slot_name in unknown_slots {
217 children_by_slot.remove(&slot_name);
218 }
219
220 let mut move_children_into_popup = None;
221 let forwarded_sources_by_target: HashMap<SmolStr, Vec<SmolStr>> =
222 elem_mut.forwarded_slots.iter().fold(HashMap::new(), |mut map, forwarding| {
223 map.entry(crate::parser::normalize_identifier(forwarding.target.as_str()))
224 .or_default()
225 .push(crate::parser::normalize_identifier(forwarding.source.as_str()));
226 map
227 });
228
229 struct SlotInsertion {
230 slot_name: SmolStr,
231 insertion_index: usize,
232 node: ChildInsertionPointNode,
233 insertion_element: ElementRc,
234 children: Vec<ElementRc>,
235 }
236
237 let mut insertions_by_parent: HashMap<ByAddress<ElementRc>, (ElementRc, Vec<SlotInsertion>)> =
239 HashMap::new();
240
241 for (slot_name, inlined_cip) in inlined_insertion_points.iter() {
242 let children = children_by_slot.remove(slot_name.as_str()).unwrap_or_default();
243 if let Some(insertion_element) = mapping.get(&element_key(inlined_cip.parent.clone())) {
244 insertions_by_parent
245 .entry(element_key(insertion_element.clone()))
246 .or_insert_with(|| (insertion_element.clone(), Vec::new()))
247 .1
248 .push(SlotInsertion {
249 slot_name: slot_name.as_str().into(),
250 insertion_index: inlined_cip.insertion_index,
251 node: inlined_cip.node.clone(),
252 insertion_element: insertion_element.clone(),
253 children,
254 });
255 } else if !children.is_empty() {
256 debug_assert!(inlined_component.popup_windows.borrow().iter().any(|p| Rc::ptr_eq(
258 &p.component,
259 &inlined_cip.parent.borrow().enclosing_component.upgrade().unwrap()
260 )));
261 if slot_name == DEFAULT_SLOT_NAME {
262 move_children_into_popup = Some(children);
263 } else {
264 diag.push_error(
265 format!("The slot '{slot_name}' cannot appear in a PopupWindow"),
266 &inlined_cip.node,
267 );
268 }
269 }
270 }
271
272 let mut insertions_for_parent =
274 |insertion_element: &ElementRc, insertions: &mut Vec<SlotInsertion>| {
275 insertions.sort_by(|a, b| {
276 a.insertion_index
277 .cmp(&b.insertion_index)
278 .then_with(|| a.node.span().offset.cmp(&b.node.span().offset))
279 .then_with(|| a.slot_name.cmp(&b.slot_name))
280 });
281
282 let mut offset = 0usize;
283 if Rc::ptr_eq(elem, insertion_element) {
284 for insertion in insertions.drain(..) {
286 let adjusted_index = insertion.insertion_index + offset;
287 let inserted_len = insertion.children.len();
288 if inserted_len > 0 {
289 new_children.splice(adjusted_index..adjusted_index, insertion.children);
290 }
291
292 let mut root_insertion_points =
293 root_component.child_insertion_points.borrow_mut();
294 for (root_slot_name, cip) in root_insertion_points.iter_mut() {
295 let forwarded_match = forwarded_sources_by_target
296 .get(insertion.slot_name.as_str())
297 .is_some_and(|sources| {
298 sources.iter().any(|source| source == root_slot_name)
299 });
300 if Rc::ptr_eq(&cip.parent, elem)
301 && (root_slot_name.as_str() == insertion.slot_name.as_str()
302 || forwarded_match)
303 {
304 *cip = ChildrenInsertionPoint {
305 parent: insertion.insertion_element.clone(),
306 insertion_index: adjusted_index + cip.insertion_index,
307 node: insertion.node.clone(),
308 };
309 }
310 }
311 if root_insertion_points.is_empty()
312 && Rc::ptr_eq(elem, &root_component.root_element)
313 && insertion.slot_name == DEFAULT_SLOT_NAME
314 {
315 root_insertion_points.insert(
316 DEFAULT_SLOT_NAME.into(),
317 ChildrenInsertionPoint {
318 parent: insertion.insertion_element.clone(),
319 insertion_index: adjusted_index + inserted_len,
320 node: insertion.node.clone(),
321 },
322 );
323 }
324
325 offset += inserted_len;
326 }
327 } else {
328 let mut insertion_element_mut = insertion_element.borrow_mut();
330 for insertion in insertions.drain(..) {
331 let adjusted_index = insertion.insertion_index + offset;
332 let inserted_len = insertion.children.len();
333 if inserted_len > 0 {
334 insertion_element_mut
335 .children
336 .splice(adjusted_index..adjusted_index, insertion.children);
337 }
338
339 let mut root_insertion_points =
340 root_component.child_insertion_points.borrow_mut();
341 for (root_slot_name, cip) in root_insertion_points.iter_mut() {
342 let forwarded_match = forwarded_sources_by_target
343 .get(insertion.slot_name.as_str())
344 .is_some_and(|sources| {
345 sources.iter().any(|source| source == root_slot_name)
346 });
347 if Rc::ptr_eq(&cip.parent, elem)
348 && (root_slot_name.as_str() == insertion.slot_name.as_str()
349 || forwarded_match)
350 {
351 *cip = ChildrenInsertionPoint {
352 parent: insertion.insertion_element.clone(),
353 insertion_index: adjusted_index + cip.insertion_index,
354 node: insertion.node.clone(),
355 };
356 }
357 }
358 if root_insertion_points.is_empty()
359 && Rc::ptr_eq(elem, &root_component.root_element)
360 && insertion.slot_name == DEFAULT_SLOT_NAME
361 {
362 root_insertion_points.insert(
363 DEFAULT_SLOT_NAME.into(),
364 ChildrenInsertionPoint {
365 parent: insertion.insertion_element.clone(),
366 insertion_index: adjusted_index + inserted_len,
367 node: insertion.node.clone(),
368 },
369 );
370 }
371
372 offset += inserted_len;
373 }
374 }
375 };
376
377 for (insertion_element, mut insertions) in insertions_by_parent.into_values() {
378 insertions_for_parent(&insertion_element, &mut insertions);
379 }
380
381 elem_mut.children = new_children;
382 elem_mut.debug.extend_from_slice(&inlined_component.root_element.borrow().debug);
383
384 if let ElementType::Component(c) = &mut elem_mut.base_type
385 && c.parent_element().is_some()
386 {
387 debug_assert!(Rc::ptr_eq(elem, &c.parent_element().unwrap()));
388 *c = duplicate_sub_component(c, elem, &mut mapping, priority_delta);
389 };
390
391 root_component.optimized_elements.borrow_mut().extend(
392 inlined_component.optimized_elements.borrow().iter().map(|x| {
393 duplicate_element_with_mapping(x, &mut mapping, root_component, priority_delta)
394 }),
395 );
396 root_component.popup_windows.borrow_mut().extend(
397 inlined_component
398 .popup_windows
399 .borrow()
400 .iter()
401 .map(|p| duplicate_popup(p, &mut mapping, priority_delta)),
402 );
403
404 root_component.menu_item_tree.borrow_mut().extend(
405 inlined_component
406 .menu_item_tree
407 .borrow()
408 .iter()
409 .map(|it| duplicate_sub_component(it, elem, &mut mapping, priority_delta)),
410 );
411
412 root_component.timers.borrow_mut().extend(inlined_component.timers.borrow().iter().map(|t| {
413 let inlined_element = mapping.get(&element_key(t.element.upgrade().unwrap())).unwrap();
414
415 Timer { element: Rc::downgrade(inlined_element), ..t.clone() }
416 }));
417
418 let mut moved_into_popup = HashSet::new();
419 if let Some(children) = move_children_into_popup {
420 let inlined_insertion_points = inlined_component.child_insertion_points.borrow();
421 let inlined_cip = inlined_insertion_points.get(DEFAULT_SLOT_NAME).unwrap();
422
423 let insertion_element = mapping.get(&element_key(inlined_cip.parent.clone())).unwrap();
424 debug_assert!(!std::rc::Weak::ptr_eq(
425 &insertion_element.borrow().enclosing_component,
426 &elem_mut.enclosing_component,
427 ));
428 debug_assert!(root_component.popup_windows.borrow().iter().any(|p| Rc::ptr_eq(
429 &p.component,
430 &insertion_element.borrow().enclosing_component.upgrade().unwrap()
431 )));
432 for c in &children {
433 recurse_elem(c, &(), &mut |e, _| {
434 e.borrow_mut().enclosing_component =
435 insertion_element.borrow().enclosing_component.clone();
436 moved_into_popup.insert(element_key(e.clone()));
437 });
438 }
439 insertion_element
440 .borrow_mut()
441 .children
442 .splice(inlined_cip.insertion_index..inlined_cip.insertion_index, children);
443 let mut root_insertion_points = root_component.child_insertion_points.borrow_mut();
444 for cip in root_insertion_points.values_mut() {
445 if Rc::ptr_eq(&cip.parent, elem) {
446 *cip = ChildrenInsertionPoint {
447 parent: insertion_element.clone(),
448 insertion_index: inlined_cip.insertion_index + cip.insertion_index,
449 node: inlined_cip.node.clone(),
450 };
451 }
452 }
453 if root_insertion_points.is_empty() {
454 root_insertion_points.insert(
455 DEFAULT_SLOT_NAME.into(),
456 ChildrenInsertionPoint {
457 parent: insertion_element.clone(),
458 insertion_index: inlined_cip.insertion_index,
459 node: inlined_cip.node.clone(),
460 },
461 );
462 };
463 }
464
465 for (property, root_binding) in
466 inlined_component.root_element.borrow().bindings_including_synthetic()
467 {
468 match elem_mut.binding_cell_including_synthetic(property) {
469 Some(elem_binding) => {
470 let mut binding = elem_binding.borrow_mut();
471 if binding.merge_with(&root_binding.borrow()) {
472 binding.priority = binding.priority.saturating_add(priority_delta);
473 }
474 }
475 None => {
476 let mut elem_binding = root_binding.borrow().clone();
477 elem_binding.priority = elem_binding.priority.saturating_add(priority_delta);
478 elem_mut.set_binding(property.clone(), elem_binding);
479 }
480 }
481 }
482 for (k, val) in inlined_component.root_element.borrow().change_callbacks.iter() {
483 match elem_mut.change_callbacks.entry(k.clone()) {
484 std::collections::btree_map::Entry::Vacant(entry) => {
485 entry.insert(val.clone());
486 }
487 std::collections::btree_map::Entry::Occupied(mut entry) => {
488 entry.get_mut().get_mut().splice(0..0, val.borrow().iter().cloned());
489 }
490 }
491 }
492
493 if let Some(orig) = &inlined_component.root_element.borrow().layout_info_prop {
494 if let Some(_new) = &mut elem_mut.layout_info_prop {
495 todo!("Merge layout infos");
496 } else {
497 elem_mut.layout_info_prop = Some(orig.clone());
498 }
499 }
500 if let Some(orig) = &inlined_component.root_element.borrow().layout_info_h_at_own_height {
501 if let Some(_new) = &mut elem_mut.layout_info_h_at_own_height {
502 todo!("Merge layout infos");
503 } else {
504 elem_mut.layout_info_h_at_own_height = Some(orig.clone());
505 }
506 }
507 if let Some(orig) = &inlined_component.root_element.borrow().layout_info_v_with_constraint {
508 if let Some(_new) = &mut elem_mut.layout_info_v_with_constraint {
509 todo!("Merge layout infos");
510 } else {
511 elem_mut.layout_info_v_with_constraint = Some(orig.clone());
512 }
513 }
514
515 core::mem::drop(elem_mut);
516
517 let fixup_init_expression = |mut init_code: Expression| {
518 visit_named_references_in_expression(&mut init_code, &mut |nr| {
520 fixup_reference(nr, &mapping)
521 });
522 fixup_element_references(&mut init_code, &mapping);
523 init_code
524 };
525 let inlined_init_code = inlined_component
526 .init_code
527 .borrow()
528 .inlined_init_code
529 .values()
530 .cloned()
531 .chain(inlined_component.init_code.borrow().constructor_code.iter().cloned())
532 .map(fixup_init_expression)
533 .collect();
534
535 root_component
536 .init_code
537 .borrow_mut()
538 .inlined_init_code
539 .insert(elem.borrow().span().offset, Expression::CodeBlock(inlined_init_code));
540
541 for e in mapping.values() {
543 visit_element_expressions(e, |expr, _, _| fixup_element_references(expr, &mapping));
546 for d in &mut e.borrow_mut().debug {
549 if let Some(crate::layout::Layout::GridLayout(grid)) = d.layout.as_mut() {
550 grid.clone_cells();
551 }
552 }
553 visit_all_named_references_in_element(e, |nr| fixup_reference(nr, &mapping));
554 }
555 for p in root_component.popup_windows.borrow_mut().iter_mut() {
556 fixup_reference(&mut p.x, &mapping);
557 fixup_reference(&mut p.y, &mapping);
558 if let Some(is_open) = &mut p.is_open {
559 fixup_reference(is_open, &mapping);
560 }
561 }
562 for t in root_component.timers.borrow_mut().iter_mut() {
563 fixup_reference(&mut t.interval, &mapping);
564 fixup_reference(&mut t.running, &mapping);
565 fixup_reference(&mut t.triggered, &mapping);
566 }
567 if !moved_into_popup.is_empty() {
569 recurse_elem_no_borrow(&root_component.root_element.clone(), &(), &mut |e, _| {
570 if !moved_into_popup.contains(&element_key(e.clone())) {
571 visit_all_named_references_in_element(e, |nr| {
572 if moved_into_popup.contains(&element_key(nr.element())) {
573 diag.push_error(format!("Access to property '{nr:?}' which is inlined into a PopupWindow via @children is forbidden"), &*e.borrow());
574 }
575 });
576 }
577 });
578 }
579}
580
581fn duplicate_element_with_mapping(
583 element: &ElementRc,
584 mapping: &mut Mapping,
585 root_component: &Rc<Component>,
586 priority_delta: i32,
587) -> ElementRc {
588 let elem = element.borrow();
589 let new = Rc::new(RefCell::new(Element {
590 base_type: elem.base_type.clone(),
591 id: elem.id.clone(),
592 is_injected_wrapper_element: elem.is_injected_wrapper_element,
593 property_declarations: elem.property_declarations.clone(),
594 shadowing_members: elem.shadowing_members.clone(),
595 bindings: elem
597 .bindings_including_synthetic()
598 .map(|b| duplicate_binding(b, mapping, root_component, priority_delta))
599 .collect(),
600 change_callbacks: elem.change_callbacks.clone(),
601 property_analysis: elem.property_analysis.clone(),
602 children: elem
603 .children
604 .iter()
605 .map(|x| duplicate_element_with_mapping(x, mapping, root_component, priority_delta))
606 .collect(),
607 repeated: elem.repeated.clone(),
608 is_component_placeholder: elem.is_component_placeholder,
609 debug: elem.debug.clone(),
610 enclosing_component: Rc::downgrade(root_component),
611 states: elem.states.clone(),
612 match_elements: Default::default(),
613 transitions: elem
614 .transitions
615 .iter()
616 .map(|t| duplicate_transition(t, mapping, root_component, priority_delta))
617 .collect(),
618 child_of_layout: elem.child_of_layout,
619 child_of_flexbox: elem.child_of_flexbox,
620 parent_box_layout_orientation: elem.parent_box_layout_orientation,
621 layout_info_prop: elem.layout_info_prop.clone(),
622 layout_info_v_with_constraint: elem.layout_info_v_with_constraint.clone(),
623 layout_info_h_at_own_height: elem.layout_info_h_at_own_height.clone(),
624 height_is_literal: elem.height_is_literal,
625 default_fill_parent: elem.default_fill_parent,
626 accessibility_props: elem.accessibility_props.clone(),
627 geometry_props: elem.geometry_props.clone(),
628 named_references: Default::default(),
629 item_index: Default::default(), item_index_of_first_children: Default::default(),
631 is_flickable_content: elem.is_flickable_content,
632 has_popup_child: elem.has_popup_child,
633 is_tooltip: elem.is_tooltip,
634 z_order: elem.z_order.clone(),
635 is_legacy_syntax: elem.is_legacy_syntax,
636 inline_depth: elem.inline_depth + 1,
637 slot_target: elem.slot_target.clone(),
638 forwarded_slots: elem.forwarded_slots.clone(),
639 grid_layout_cell: elem
643 .grid_layout_cell
644 .as_ref()
645 .map(|cell| Rc::new(RefCell::new(cell.borrow().clone()))),
646 }));
647 mapping.insert(element_key(element.clone()), new.clone());
648 if let ElementType::Component(c) = &mut new.borrow_mut().base_type
649 && c.parent_element().is_some()
650 {
651 debug_assert!(Rc::ptr_eq(element, &c.parent_element().unwrap()));
652 *c = duplicate_sub_component(c, &new, mapping, priority_delta);
653 };
654
655 new
656}
657
658fn duplicate_sub_component(
660 component_to_duplicate: &Rc<Component>,
661 new_parent: &ElementRc,
662 mapping: &mut Mapping,
663 priority_delta: i32,
664) -> Rc<Component> {
665 debug_assert!(component_to_duplicate.parent_element().is_some());
666 let new_component = Component {
667 node: component_to_duplicate.node.clone(),
668 id: component_to_duplicate.id.clone(),
669 root_element: duplicate_element_with_mapping(
670 &component_to_duplicate.root_element,
671 mapping,
672 component_to_duplicate, priority_delta,
674 ),
675 parent_element: RefCell::new(Rc::downgrade(new_parent)),
676 optimized_elements: RefCell::new(
677 component_to_duplicate
678 .optimized_elements
679 .borrow()
680 .iter()
681 .map(|e| {
682 duplicate_element_with_mapping(
683 e,
684 mapping,
685 component_to_duplicate,
686 priority_delta,
687 )
688 })
689 .collect(),
690 ),
691 root_constraints: component_to_duplicate.root_constraints.clone(),
692 child_insertion_points: component_to_duplicate.child_insertion_points.clone(),
693 declared_slots: component_to_duplicate.declared_slots.clone(),
694 init_code: component_to_duplicate.init_code.clone(),
695 popup_windows: Default::default(),
696 timers: component_to_duplicate.timers.clone(),
697 menu_item_tree: Default::default(),
698 exported_global_names: component_to_duplicate.exported_global_names.clone(),
699 used: component_to_duplicate.used.clone(),
700 private_properties: Default::default(),
701 inherits_popup_window: core::cell::Cell::new(false),
702 from_library: core::cell::Cell::new(false),
703 };
704
705 let new_component = Rc::new(new_component);
706 let weak = Rc::downgrade(&new_component);
707 recurse_elem(&new_component.root_element, &(), &mut |e, _| {
708 e.borrow_mut().enclosing_component = weak.clone()
709 });
710 for o in new_component.optimized_elements.borrow().iter() {
711 o.borrow_mut().enclosing_component = weak.clone()
712 }
713 *new_component.popup_windows.borrow_mut() = component_to_duplicate
714 .popup_windows
715 .borrow()
716 .iter()
717 .map(|p| duplicate_popup(p, mapping, priority_delta))
718 .collect();
719 for p in new_component.popup_windows.borrow_mut().iter_mut() {
720 fixup_reference(&mut p.x, mapping);
721 fixup_reference(&mut p.y, mapping);
722 if let Some(is_open) = &mut p.is_open {
723 fixup_reference(is_open, mapping);
724 }
725 }
726 for t in new_component.timers.borrow_mut().iter_mut() {
727 fixup_reference(&mut t.interval, mapping);
728 fixup_reference(&mut t.running, mapping);
729 fixup_reference(&mut t.triggered, mapping);
730 if let Some(e) = mapping.get(&element_key(t.element.upgrade().unwrap())) {
731 t.element = Rc::downgrade(e);
732 }
733 }
734 *new_component.menu_item_tree.borrow_mut() = component_to_duplicate
735 .menu_item_tree
736 .borrow()
737 .iter()
738 .map(|it| {
739 let new_parent =
740 mapping.get(&element_key(it.parent_element().unwrap())).unwrap().clone();
741 duplicate_sub_component(it, &new_parent, mapping, priority_delta)
742 })
743 .collect();
744 new_component
745 .root_constraints
746 .borrow_mut()
747 .visit_named_references(&mut |nr| fixup_reference(nr, mapping));
748 new_component
749}
750
751fn duplicate_popup(p: &PopupWindow, mapping: &mut Mapping, priority_delta: i32) -> PopupWindow {
752 let parent = mapping
753 .get(&element_key(p.component.parent_element().expect("must have a parent")))
754 .expect("Parent must be in the mapping")
755 .clone();
756 PopupWindow {
757 x: p.x.clone(),
758 y: p.y.clone(),
759 close_policy: p.close_policy.clone(),
760 component: duplicate_sub_component(&p.component, &parent, mapping, priority_delta),
761 parent_element: mapping
762 .get(&element_key(p.parent_element.clone()))
763 .expect("Parent element must be in the mapping")
764 .clone(),
765 is_tooltip: p.is_tooltip,
766 is_open: p.is_open.clone(),
767 }
768}
769
770fn duplicate_binding(
773 (k, b): (&SmolStr, &RefCell<BindingExpression>),
774 mapping: &mut Mapping,
775 root_component: &Rc<Component>,
776 priority_delta: i32,
777) -> (SmolStr, RefCell<BindingExpression>) {
778 let b = b.borrow();
779 let b = BindingExpression {
780 expression: b.expression.clone(),
781 span: b.span.clone(),
782 priority: b.priority.saturating_add(priority_delta),
783 animation: b
784 .animation
785 .as_ref()
786 .map(|pa| duplicate_property_animation(pa, mapping, root_component, priority_delta)),
787 analysis: b.analysis.clone(),
788 two_way_bindings: b.two_way_bindings.clone(),
789 };
790 (k.clone(), b.into())
791}
792
793fn duplicate_property_animation(
794 v: &PropertyAnimation,
795 mapping: &mut Mapping,
796 root_component: &Rc<Component>,
797 priority_delta: i32,
798) -> PropertyAnimation {
799 match v {
800 PropertyAnimation::Static(a) => PropertyAnimation::Static(duplicate_element_with_mapping(
801 a,
802 mapping,
803 root_component,
804 priority_delta,
805 )),
806 PropertyAnimation::Transition { state_ref, animations } => PropertyAnimation::Transition {
807 state_ref: state_ref.clone(),
808 animations: animations
809 .iter()
810 .map(|a| TransitionPropertyAnimation {
811 state_id: a.state_id,
812 direction: a.direction,
813 animation: duplicate_element_with_mapping(
814 &a.animation,
815 mapping,
816 root_component,
817 priority_delta,
818 ),
819 })
820 .collect(),
821 },
822 }
823}
824
825fn fixup_reference(nr: &mut NamedReference, mapping: &Mapping) {
826 if let Some(e) = mapping.get(&element_key(nr.element())) {
827 *nr = NamedReference::new(e, nr.name().clone());
828 }
829}
830
831fn fixup_grid_layout(layout: &mut crate::layout::GridLayout, fxe: &impl Fn(&mut ElementRc)) {
834 for e in &mut layout.elems {
835 fxe(&mut e.item.element);
836 }
837 layout.clone_cells();
840 for elem in &mut layout.elems {
841 let mut cell = elem.cell.borrow_mut();
842 let Some(child_items) = &mut cell.child_items else { continue };
843 for child in child_items.iter_mut() {
844 fxe(&mut child.layout_item_mut().element);
845 if let crate::layout::RowChildTemplate::Repeated { repeated_element, .. } = child {
846 fxe(repeated_element);
847 }
848 }
849 }
850}
851
852fn fixup_element_references(expr: &mut Expression, mapping: &Mapping) {
853 let fx = |element: &mut std::rc::Weak<RefCell<Element>>| {
854 if let Some(e) = element.upgrade().and_then(|e| mapping.get(&element_key(e))) {
855 *element = Rc::downgrade(e);
856 }
857 };
858 let fxe = |element: &mut ElementRc| {
859 if let Some(e) = mapping.get(&element_key(element.clone())) {
860 *element = e.clone();
861 }
862 };
863 match expr {
864 Expression::ElementReference(element) => fx(element),
865 Expression::SolveBoxLayout(layout, _) => {
866 for e in &mut layout.elems {
867 fxe(&mut e.element);
868 }
869 }
870 Expression::ComputeBoxLayoutInfo { layout, cross_axis_size, .. } => {
871 for e in &mut layout.elems {
872 fxe(&mut e.element);
873 }
874 if let Some(cas) = cross_axis_size {
875 fixup_element_references(cas, mapping);
876 }
877 }
878 Expression::SolveGridLayout { layout, .. } | Expression::OrganizeGridLayout(layout) => {
879 fixup_grid_layout(layout, &fxe);
880 }
881 Expression::ComputeGridLayoutInfo { layout, cross_axis_size, .. } => {
882 fixup_grid_layout(layout, &fxe);
883 if let Some(cas) = cross_axis_size {
884 fixup_element_references(cas, mapping);
885 }
886 }
887 Expression::SolveFlexboxLayout(layout) => {
888 for e in &mut layout.elems {
889 fxe(&mut e.element);
890 }
891 }
892 Expression::ComputeFlexboxLayoutInfo { layout, cross_axis_size, .. } => {
893 for e in &mut layout.elems {
894 fxe(&mut e.element);
895 }
896 if let Some(cas) = cross_axis_size {
897 fixup_element_references(cas, mapping);
898 }
899 }
900 Expression::RepeaterModelReference { element }
901 | Expression::RepeaterIndexReference { element } => fx(element),
902 _ => expr.visit_mut(|e| fixup_element_references(e, mapping)),
903 }
904}
905
906fn duplicate_transition(
907 t: &Transition,
908 mapping: &mut HashMap<ByAddress<ElementRc>, Rc<RefCell<Element>>>,
909 root_component: &Rc<Component>,
910 priority_delta: i32,
911) -> Transition {
912 Transition {
913 direction: t.direction,
914 state_id: t.state_id.clone(),
915 property_animations: t
916 .property_animations
917 .iter()
918 .map(|(r, loc, anim)| {
919 (
920 r.clone(),
921 loc.clone(),
922 duplicate_element_with_mapping(anim, mapping, root_component, priority_delta),
923 )
924 })
925 .collect(),
926 node: t.node.clone(),
927 }
928}
929
930fn component_requires_inlining(component: &Rc<Component>) -> bool {
933 let root_element = &component.root_element;
934 if super::flickable::is_flickable_element(root_element) {
935 return true;
936 }
937
938 for (prop, binding) in root_element.borrow().real_bindings() {
939 let binding = binding.borrow();
940 if prop.starts_with("drop-shadow-")
943 || prop.starts_with("inner-shadow-")
944 || prop == "opacity"
945 || prop == "cache-rendering-hint"
946 || prop == "visible"
947 {
948 return true;
949 }
950 if (prop == "height" || prop == "width") && binding.expression.ty() == Type::Percent {
951 return true;
953 }
954 if binding.animation.is_some() {
955 let lookup_result =
956 root_element.borrow().lookup_property(prop, PropertyLookupMode::InternalName);
957 if !lookup_result.is_valid()
958 || !lookup_result.is_local_to_component
959 || !matches!(
960 lookup_result.property_visibility,
961 PropertyVisibility::Private | PropertyVisibility::Output
962 )
963 {
964 return true;
967 }
968 }
969 }
970
971 false
972}
973
974fn element_require_inlining(elem: &ElementRc) -> bool {
975 if !elem.borrow().children.is_empty() {
976 return true;
978 }
979
980 if !elem.borrow().forwarded_slots.is_empty() {
981 return true;
984 }
985
986 if super::lower_popups::is_popup_window(elem) {
988 return true;
989 }
990
991 for (prop, binding) in elem.borrow().real_bindings() {
992 if prop == "clip" {
993 return true;
995 }
996
997 if (prop == "padding"
998 || prop == "spacing"
999 || prop.starts_with("padding-")
1000 || prop.starts_with("spacing-")
1001 || prop == "alignment")
1002 && let ElementType::Component(base) = &elem.borrow().base_type
1003 && crate::layout::is_layout(&base.root_element.borrow().base_type)
1004 && !base.root_element.borrow().is_binding_set(prop, false)
1005 {
1006 return true;
1008 }
1009
1010 let binding = binding.borrow();
1011 if binding.animation.is_some() && matches!(binding.expression, Expression::Invalid) {
1012 return true;
1014 }
1015 }
1016
1017 false
1018}