1use crate::diagnostics::{BuildDiagnostics, Spanned};
7use crate::expression_tree::{BindingExpression, Expression, NamedReference};
8use crate::langtype::{ElementType, Type};
9use crate::object_tree::*;
10use by_address::ByAddress;
11use smol_str::SmolStr;
12use std::cell::RefCell;
13use std::collections::{HashMap, HashSet};
14use std::rc::Rc;
15
16#[derive(Copy, Clone, Eq, PartialEq)]
17pub enum InlineSelection {
18 InlineAllComponents,
19 InlineOnlyRequiredComponents,
20}
21
22pub fn inline(doc: &Document, inline_selection: InlineSelection, diag: &mut BuildDiagnostics) {
23 fn inline_components_recursively(
24 component: &Rc<Component>,
25 roots: &HashSet<ByAddress<Rc<Component>>>,
26 inline_selection: InlineSelection,
27 diag: &mut BuildDiagnostics,
28 ) {
29 recurse_elem_no_borrow(&component.root_element, &(), &mut |elem, _| {
30 let base = elem.borrow().base_type.clone();
31 if let ElementType::Component(c) = base {
32 inline_components_recursively(&c, roots, inline_selection, diag);
34
35 if c.parent_element.upgrade().is_some() {
36 return;
38 }
39
40 if match inline_selection {
42 InlineSelection::InlineAllComponents => true,
43 InlineSelection::InlineOnlyRequiredComponents => {
44 component_requires_inlining(&c)
45 || element_require_inlining(elem)
46 || component.parent_element.upgrade().is_none() && Rc::ptr_eq(elem, &component.root_element)
49 || roots.contains(&ByAddress(c.clone()))
51 }
52 } {
53 inline_element(elem, &c, component, diag);
54 }
55 }
56 });
57 component.popup_windows.borrow().iter().for_each(|p| {
58 inline_components_recursively(&p.component, roots, inline_selection, diag)
59 })
60 }
61 let mut roots = HashSet::new();
62 if inline_selection == InlineSelection::InlineOnlyRequiredComponents {
63 for component in doc.exported_roots().chain(doc.popup_menu_impl.iter().cloned()) {
64 roots.insert(ByAddress(component.clone()));
65 }
66 }
67 for component in doc.exported_roots().chain(doc.popup_menu_impl.iter().cloned()) {
68 inline_components_recursively(&component, &roots, inline_selection, diag);
69 let mut init_code = component.init_code.borrow_mut();
70 let inlined_init_code = core::mem::take(&mut init_code.inlined_init_code);
71 init_code.constructor_code.splice(0..0, inlined_init_code.into_values());
72 }
73}
74
75fn element_key(e: ElementRc) -> ByAddress<ElementRc> {
76 ByAddress(e)
77}
78
79type Mapping = HashMap<ByAddress<ElementRc>, ElementRc>;
80
81fn inline_element(
82 elem: &ElementRc,
83 inlined_component: &Rc<Component>,
84 root_component: &Rc<Component>,
85 diag: &mut BuildDiagnostics,
86) {
87 debug_assert_eq!(elem.borrow().base_type, ElementType::Component(inlined_component.clone()));
89 debug_assert!(
90 inlined_component.root_element.borrow().repeated.is_none(),
91 "root element of a component cannot be repeated"
92 );
93 debug_assert!(inlined_component.parent_element.upgrade().is_none());
94
95 let mut elem_mut = elem.borrow_mut();
96 let priority_delta = 1 + elem_mut.inline_depth;
97 elem_mut.base_type = inlined_component.root_element.borrow().base_type.clone();
98 elem_mut.property_declarations.extend(
99 inlined_component.root_element.borrow().property_declarations.iter().map(|(name, decl)| {
100 let mut decl = decl.clone();
101 decl.expose_in_public_api = false;
102 (name.clone(), decl)
103 }),
104 );
105
106 for (p, a) in inlined_component.root_element.borrow().property_analysis.borrow().iter() {
107 elem_mut.property_analysis.borrow_mut().entry(p.clone()).or_default().merge_with_base(a);
108 }
109
110 debug_assert!(inlined_component.root_element.borrow().states.is_empty());
112 debug_assert!(inlined_component.root_element.borrow().transitions.is_empty());
113
114 let mut mapping = HashMap::new();
116 mapping.insert(element_key(inlined_component.root_element.clone()), elem.clone());
117
118 let mut new_children = Vec::with_capacity(
119 elem_mut.children.len() + inlined_component.root_element.borrow().children.len(),
120 );
121 new_children.extend(
122 inlined_component.root_element.borrow().children.iter().map(|x| {
123 duplicate_element_with_mapping(x, &mut mapping, root_component, priority_delta)
124 }),
125 );
126
127 let mut move_children_into_popup = None;
128
129 match inlined_component.child_insertion_point.borrow().as_ref() {
130 Some(inlined_cip) => {
131 let children = std::mem::take(&mut elem_mut.children);
132 let old_count = children.len();
133 if let Some(insertion_element) = mapping.get(&element_key(inlined_cip.parent.clone())) {
134 if old_count > 0 {
135 if !Rc::ptr_eq(elem, insertion_element) {
136 debug_assert!(std::rc::Weak::ptr_eq(
137 &insertion_element.borrow().enclosing_component,
138 &elem_mut.enclosing_component,
139 ));
140 insertion_element.borrow_mut().children.splice(
141 inlined_cip.insertion_index..inlined_cip.insertion_index,
142 children,
143 );
144 } else {
145 new_children.splice(
146 inlined_cip.insertion_index..inlined_cip.insertion_index,
147 children,
148 );
149 }
150 }
151 let mut cip = root_component.child_insertion_point.borrow_mut();
152 if let Some(cip) = cip.as_mut() {
153 if Rc::ptr_eq(&cip.parent, elem) {
154 *cip = ChildrenInsertionPoint {
155 parent: insertion_element.clone(),
156 insertion_index: inlined_cip.insertion_index + cip.insertion_index,
157 node: inlined_cip.node.clone(),
158 };
159 }
160 } else if Rc::ptr_eq(elem, &root_component.root_element) {
161 *cip = Some(ChildrenInsertionPoint {
162 parent: insertion_element.clone(),
163 insertion_index: inlined_cip.insertion_index + old_count,
164 node: inlined_cip.node.clone(),
165 });
166 };
167 } else if old_count > 0 {
168 debug_assert!(inlined_component.popup_windows.borrow().iter().any(|p| Rc::ptr_eq(
170 &p.component,
171 &inlined_cip.parent.borrow().enclosing_component.upgrade().unwrap()
172 )));
173 move_children_into_popup = Some(children);
174 };
175 }
176 _ => {
177 new_children.append(&mut elem_mut.children);
178 }
179 }
180
181 elem_mut.children = new_children;
182 elem_mut.debug.extend_from_slice(&inlined_component.root_element.borrow().debug);
183
184 if let ElementType::Component(c) = &mut elem_mut.base_type {
185 if c.parent_element.upgrade().is_some() {
186 debug_assert!(Rc::ptr_eq(elem, &c.parent_element.upgrade().unwrap()));
187 *c = duplicate_sub_component(c, elem, &mut mapping, priority_delta);
188 }
189 };
190
191 root_component.optimized_elements.borrow_mut().extend(
192 inlined_component.optimized_elements.borrow().iter().map(|x| {
193 duplicate_element_with_mapping(x, &mut mapping, root_component, priority_delta)
194 }),
195 );
196 root_component.popup_windows.borrow_mut().extend(
197 inlined_component
198 .popup_windows
199 .borrow()
200 .iter()
201 .map(|p| duplicate_popup(p, &mut mapping, priority_delta)),
202 );
203
204 root_component.timers.borrow_mut().extend(inlined_component.timers.borrow().iter().cloned());
205
206 let mut moved_into_popup = HashSet::new();
207 if let Some(children) = move_children_into_popup {
208 let child_insertion_point = inlined_component.child_insertion_point.borrow();
209 let inlined_cip = child_insertion_point.as_ref().unwrap();
210
211 let insertion_element = mapping.get(&element_key(inlined_cip.parent.clone())).unwrap();
212 debug_assert!(!std::rc::Weak::ptr_eq(
213 &insertion_element.borrow().enclosing_component,
214 &elem_mut.enclosing_component,
215 ));
216 debug_assert!(root_component.popup_windows.borrow().iter().any(|p| Rc::ptr_eq(
217 &p.component,
218 &insertion_element.borrow().enclosing_component.upgrade().unwrap()
219 )));
220 for c in &children {
221 recurse_elem(c, &(), &mut |e, _| {
222 e.borrow_mut().enclosing_component =
223 insertion_element.borrow().enclosing_component.clone();
224 moved_into_popup.insert(element_key(e.clone()));
225 });
226 }
227 insertion_element
228 .borrow_mut()
229 .children
230 .splice(inlined_cip.insertion_index..inlined_cip.insertion_index, children);
231 let mut cip = root_component.child_insertion_point.borrow_mut();
232 if let Some(cip) = cip.as_mut() {
233 if Rc::ptr_eq(&cip.parent, elem) {
234 *cip = ChildrenInsertionPoint {
235 parent: insertion_element.clone(),
236 insertion_index: inlined_cip.insertion_index + cip.insertion_index,
237 node: inlined_cip.node.clone(),
238 };
239 }
240 } else {
241 *cip = Some(ChildrenInsertionPoint {
242 parent: insertion_element.clone(),
243 insertion_index: inlined_cip.insertion_index,
244 node: inlined_cip.node.clone(),
245 });
246 };
247 }
248
249 for (k, val) in inlined_component.root_element.borrow().bindings.iter() {
250 match elem_mut.bindings.entry(k.clone()) {
251 std::collections::btree_map::Entry::Vacant(entry) => {
252 let priority = &mut entry.insert(val.clone()).get_mut().priority;
253 *priority = priority.saturating_add(priority_delta);
254 }
255 std::collections::btree_map::Entry::Occupied(mut entry) => {
256 let entry = entry.get_mut().get_mut();
257 if entry.merge_with(&val.borrow()) {
258 entry.priority = entry.priority.saturating_add(priority_delta);
259 }
260 }
261 }
262 }
263 for (k, val) in inlined_component.root_element.borrow().change_callbacks.iter() {
264 match elem_mut.change_callbacks.entry(k.clone()) {
265 std::collections::btree_map::Entry::Vacant(entry) => {
266 entry.insert(val.clone());
267 }
268 std::collections::btree_map::Entry::Occupied(mut entry) => {
269 entry.get_mut().get_mut().splice(0..0, val.borrow().iter().cloned());
270 }
271 }
272 }
273
274 if let Some(orig) = &inlined_component.root_element.borrow().layout_info_prop {
275 if let Some(_new) = &mut elem_mut.layout_info_prop {
276 todo!("Merge layout infos");
277 } else {
278 elem_mut.layout_info_prop = Some(orig.clone());
279 }
280 }
281
282 core::mem::drop(elem_mut);
283
284 let fixup_init_expression = |mut init_code: Expression| {
285 visit_named_references_in_expression(&mut init_code, &mut |nr| {
287 fixup_reference(nr, &mapping)
288 });
289 fixup_element_references(&mut init_code, &mapping);
290 init_code
291 };
292 let inlined_init_code = inlined_component
293 .init_code
294 .borrow()
295 .inlined_init_code
296 .values()
297 .cloned()
298 .chain(inlined_component.init_code.borrow().constructor_code.iter().cloned())
299 .map(fixup_init_expression)
300 .collect();
301
302 root_component
303 .init_code
304 .borrow_mut()
305 .inlined_init_code
306 .insert(elem.borrow().span().offset, Expression::CodeBlock(inlined_init_code));
307
308 for e in mapping.values() {
310 visit_all_named_references_in_element(e, |nr| fixup_reference(nr, &mapping));
311 visit_element_expressions(e, |expr, _, _| fixup_element_references(expr, &mapping));
312 }
313 for p in root_component.popup_windows.borrow_mut().iter_mut() {
314 fixup_reference(&mut p.x, &mapping);
315 fixup_reference(&mut p.y, &mapping);
316 }
317 for t in root_component.timers.borrow_mut().iter_mut() {
318 fixup_reference(&mut t.interval, &mapping);
319 fixup_reference(&mut t.running, &mapping);
320 fixup_reference(&mut t.triggered, &mapping);
321 }
322 if !moved_into_popup.is_empty() {
324 recurse_elem_no_borrow(&root_component.root_element.clone(), &(), &mut |e, _| {
325 if !moved_into_popup.contains(&element_key(e.clone())) {
326 visit_all_named_references_in_element(e, |nr| {
327 if moved_into_popup.contains(&element_key(nr.element())) {
328 diag.push_error(format!("Access to property '{nr:?}' which is inlined into a PopupWindow via @children is forbidden"), &*e.borrow());
329 }
330 });
331 }
332 });
333 }
334}
335
336fn duplicate_element_with_mapping(
338 element: &ElementRc,
339 mapping: &mut Mapping,
340 root_component: &Rc<Component>,
341 priority_delta: i32,
342) -> ElementRc {
343 let elem = element.borrow();
344 let new = Rc::new(RefCell::new(Element {
345 base_type: elem.base_type.clone(),
346 id: elem.id.clone(),
347 property_declarations: elem.property_declarations.clone(),
348 bindings: elem
350 .bindings
351 .iter()
352 .map(|b| duplicate_binding(b, mapping, root_component, priority_delta))
353 .collect(),
354 change_callbacks: elem.change_callbacks.clone(),
355 property_analysis: elem.property_analysis.clone(),
356 children: elem
357 .children
358 .iter()
359 .map(|x| duplicate_element_with_mapping(x, mapping, root_component, priority_delta))
360 .collect(),
361 repeated: elem.repeated.clone(),
362 is_component_placeholder: elem.is_component_placeholder,
363 debug: elem.debug.clone(),
364 enclosing_component: Rc::downgrade(root_component),
365 states: elem.states.clone(),
366 transitions: elem
367 .transitions
368 .iter()
369 .map(|t| duplicate_transition(t, mapping, root_component, priority_delta))
370 .collect(),
371 child_of_layout: elem.child_of_layout,
372 layout_info_prop: elem.layout_info_prop.clone(),
373 default_fill_parent: elem.default_fill_parent,
374 accessibility_props: elem.accessibility_props.clone(),
375 geometry_props: elem.geometry_props.clone(),
376 named_references: Default::default(),
377 item_index: Default::default(), item_index_of_first_children: Default::default(),
379 is_flickable_viewport: elem.is_flickable_viewport,
380 has_popup_child: elem.has_popup_child,
381 is_legacy_syntax: elem.is_legacy_syntax,
382 inline_depth: elem.inline_depth + 1,
383 }));
384 mapping.insert(element_key(element.clone()), new.clone());
385 if let ElementType::Component(c) = &mut new.borrow_mut().base_type {
386 if c.parent_element.upgrade().is_some() {
387 debug_assert!(Rc::ptr_eq(element, &c.parent_element.upgrade().unwrap()));
388 *c = duplicate_sub_component(c, &new, mapping, priority_delta);
389 }
390 };
391
392 new
393}
394
395fn duplicate_sub_component(
397 component_to_duplicate: &Rc<Component>,
398 new_parent: &ElementRc,
399 mapping: &mut Mapping,
400 priority_delta: i32,
401) -> Rc<Component> {
402 debug_assert!(component_to_duplicate.parent_element.upgrade().is_some());
403 let new_component = Component {
404 node: component_to_duplicate.node.clone(),
405 id: component_to_duplicate.id.clone(),
406 root_element: duplicate_element_with_mapping(
407 &component_to_duplicate.root_element,
408 mapping,
409 component_to_duplicate, priority_delta,
411 ),
412 parent_element: Rc::downgrade(new_parent),
413 optimized_elements: RefCell::new(
414 component_to_duplicate
415 .optimized_elements
416 .borrow()
417 .iter()
418 .map(|e| {
419 duplicate_element_with_mapping(
420 e,
421 mapping,
422 component_to_duplicate,
423 priority_delta,
424 )
425 })
426 .collect(),
427 ),
428 root_constraints: component_to_duplicate.root_constraints.clone(),
429 child_insertion_point: component_to_duplicate.child_insertion_point.clone(),
430 init_code: component_to_duplicate.init_code.clone(),
431 popup_windows: Default::default(),
432 timers: component_to_duplicate.timers.clone(),
433 menu_item_tree: Default::default(),
434 exported_global_names: component_to_duplicate.exported_global_names.clone(),
435 used: component_to_duplicate.used.clone(),
436 private_properties: Default::default(),
437 inherits_popup_window: core::cell::Cell::new(false),
438 };
439
440 let new_component = Rc::new(new_component);
441 let weak = Rc::downgrade(&new_component);
442 recurse_elem(&new_component.root_element, &(), &mut |e, _| {
443 e.borrow_mut().enclosing_component = weak.clone()
444 });
445 for o in new_component.optimized_elements.borrow().iter() {
446 o.borrow_mut().enclosing_component = weak.clone()
447 }
448 *new_component.popup_windows.borrow_mut() = component_to_duplicate
449 .popup_windows
450 .borrow()
451 .iter()
452 .map(|p| duplicate_popup(p, mapping, priority_delta))
453 .collect();
454 for p in new_component.popup_windows.borrow_mut().iter_mut() {
455 fixup_reference(&mut p.x, mapping);
456 fixup_reference(&mut p.y, mapping);
457 }
458 for t in new_component.timers.borrow_mut().iter_mut() {
459 fixup_reference(&mut t.interval, mapping);
460 fixup_reference(&mut t.running, mapping);
461 fixup_reference(&mut t.triggered, mapping);
462 }
463 *new_component.menu_item_tree.borrow_mut() = component_to_duplicate
464 .menu_item_tree
465 .borrow()
466 .iter()
467 .map(|it| {
468 let new_parent =
469 mapping.get(&element_key(it.parent_element.upgrade().unwrap())).unwrap().clone();
470 duplicate_sub_component(it, &new_parent, mapping, priority_delta)
471 })
472 .collect();
473 new_component
474 .root_constraints
475 .borrow_mut()
476 .visit_named_references(&mut |nr| fixup_reference(nr, mapping));
477 new_component
478}
479
480fn duplicate_popup(p: &PopupWindow, mapping: &mut Mapping, priority_delta: i32) -> PopupWindow {
481 let parent = mapping
482 .get(&element_key(p.component.parent_element.upgrade().expect("must have a parent")))
483 .expect("Parent must be in the mapping")
484 .clone();
485 PopupWindow {
486 x: p.x.clone(),
487 y: p.y.clone(),
488 close_policy: p.close_policy.clone(),
489 component: duplicate_sub_component(&p.component, &parent, mapping, priority_delta),
490 parent_element: mapping
491 .get(&element_key(p.parent_element.clone()))
492 .expect("Parent element must be in the mapping")
493 .clone(),
494 }
495}
496
497fn duplicate_binding(
500 (k, b): (&SmolStr, &RefCell<BindingExpression>),
501 mapping: &mut Mapping,
502 root_component: &Rc<Component>,
503 priority_delta: i32,
504) -> (SmolStr, RefCell<BindingExpression>) {
505 let b = b.borrow();
506 let b = BindingExpression {
507 expression: b.expression.clone(),
508 span: b.span.clone(),
509 priority: b.priority.saturating_add(priority_delta),
510 animation: b
511 .animation
512 .as_ref()
513 .map(|pa| duplicate_property_animation(pa, mapping, root_component, priority_delta)),
514 analysis: b.analysis.clone(),
515 two_way_bindings: b.two_way_bindings.clone(),
516 };
517 (k.clone(), b.into())
518}
519
520fn duplicate_property_animation(
521 v: &PropertyAnimation,
522 mapping: &mut Mapping,
523 root_component: &Rc<Component>,
524 priority_delta: i32,
525) -> PropertyAnimation {
526 match v {
527 PropertyAnimation::Static(a) => PropertyAnimation::Static(duplicate_element_with_mapping(
528 a,
529 mapping,
530 root_component,
531 priority_delta,
532 )),
533 PropertyAnimation::Transition { state_ref, animations } => PropertyAnimation::Transition {
534 state_ref: state_ref.clone(),
535 animations: animations
536 .iter()
537 .map(|a| TransitionPropertyAnimation {
538 state_id: a.state_id,
539 direction: a.direction,
540 animation: duplicate_element_with_mapping(
541 &a.animation,
542 mapping,
543 root_component,
544 priority_delta,
545 ),
546 })
547 .collect(),
548 },
549 }
550}
551
552fn fixup_reference(nr: &mut NamedReference, mapping: &Mapping) {
553 if let Some(e) = mapping.get(&element_key(nr.element())) {
554 *nr = NamedReference::new(e, nr.name().clone());
555 }
556}
557
558fn fixup_element_references(expr: &mut Expression, mapping: &Mapping) {
559 let fx = |element: &mut std::rc::Weak<RefCell<Element>>| {
560 if let Some(e) = element.upgrade().and_then(|e| mapping.get(&element_key(e))) {
561 *element = Rc::downgrade(e);
562 }
563 };
564 let fxe = |element: &mut ElementRc| {
565 if let Some(e) = mapping.get(&element_key(element.clone())) {
566 *element = e.clone();
567 }
568 };
569 match expr {
570 Expression::ElementReference(element) => fx(element),
571 Expression::SolveLayout(l, _) | Expression::ComputeLayoutInfo(l, _) => match l {
572 crate::layout::Layout::GridLayout(l) => {
573 for e in &mut l.elems {
574 fxe(&mut e.item.element);
575 }
576 }
577 crate::layout::Layout::BoxLayout(l) => {
578 for e in &mut l.elems {
579 fxe(&mut e.element);
580 }
581 }
582 },
583 Expression::RepeaterModelReference { element }
584 | Expression::RepeaterIndexReference { element } => fx(element),
585 _ => expr.visit_mut(|e| fixup_element_references(e, mapping)),
586 }
587}
588
589fn duplicate_transition(
590 t: &Transition,
591 mapping: &mut HashMap<ByAddress<ElementRc>, Rc<RefCell<Element>>>,
592 root_component: &Rc<Component>,
593 priority_delta: i32,
594) -> Transition {
595 Transition {
596 direction: t.direction,
597 state_id: t.state_id.clone(),
598 property_animations: t
599 .property_animations
600 .iter()
601 .map(|(r, loc, anim)| {
602 (
603 r.clone(),
604 loc.clone(),
605 duplicate_element_with_mapping(anim, mapping, root_component, priority_delta),
606 )
607 })
608 .collect(),
609 node: t.node.clone(),
610 }
611}
612
613fn component_requires_inlining(component: &Rc<Component>) -> bool {
616 let root_element = &component.root_element;
617 if super::flickable::is_flickable_element(root_element) {
618 return true;
619 }
620
621 for (prop, binding) in &root_element.borrow().bindings {
622 let binding = binding.borrow();
623 if prop.starts_with("drop-shadow-")
626 || prop == "opacity"
627 || prop == "cache-rendering-hint"
628 || prop == "visible"
629 {
630 return true;
631 }
632 if (prop == "height" || prop == "width") && binding.expression.ty() == Type::Percent {
633 return true;
635 }
636 if binding.animation.is_some() {
637 let lookup_result = root_element.borrow().lookup_property(prop);
638 if !lookup_result.is_valid()
639 || !lookup_result.is_local_to_component
640 || !matches!(
641 lookup_result.property_visibility,
642 PropertyVisibility::Private | PropertyVisibility::Output
643 )
644 {
645 return true;
648 }
649 }
650 }
651
652 false
653}
654
655fn element_require_inlining(elem: &ElementRc) -> bool {
656 if !elem.borrow().children.is_empty() {
657 return true;
659 }
660
661 if super::lower_popups::is_popup_window(elem) {
663 return true;
664 }
665
666 for (prop, binding) in &elem.borrow().bindings {
667 if prop == "clip" {
668 return true;
670 }
671
672 if prop == "padding"
673 || prop == "spacing"
674 || prop.starts_with("padding-")
675 || prop.starts_with("spacing-")
676 || prop == "alignment"
677 {
678 if let ElementType::Component(base) = &elem.borrow().base_type {
679 if crate::layout::is_layout(&base.root_element.borrow().base_type) {
680 if !base.root_element.borrow().is_binding_set(prop, false) {
681 return true;
683 }
684 }
685 }
686 }
687
688 let binding = binding.borrow();
689 if binding.animation.is_some() && matches!(binding.expression, Expression::Invalid) {
690 return true;
692 }
693 }
694
695 false
696}