1use lyon_path::geom::euclid::approxeq::ApproxEq;
7
8use crate::diagnostics::{BuildDiagnostics, DiagnosticLevel, Spanned};
9use crate::expression_tree::*;
10use crate::langtype::ElementType;
11use crate::langtype::Type;
12use crate::layout::*;
13use crate::object_tree::*;
14use crate::typeloader::TypeLoader;
15use crate::typeregister::{TypeRegister, layout_info_type};
16use smol_str::{SmolStr, format_smolstr};
17use std::cell::RefCell;
18use std::collections::HashSet;
19use std::rc::Rc;
20
21fn synthesize_layoutinfo_v_with_constraint_on(
25 elem: &ElementRc,
26 span: crate::diagnostics::SourceLocation,
27 body: Expression,
28) {
29 let function_ty = Type::Function(Rc::new(crate::langtype::Function {
30 return_type: crate::typeregister::layout_info_type().into(),
31 args: vec![Type::LogicalLength],
32 arg_names: vec![SmolStr::new_static("width")],
33 }));
34 let prop_name = SmolStr::new_static("layoutinfo-v-with-constraint");
35 let nr = crate::namedreference::NamedReference::new(elem, prop_name.clone());
36
37 let mut elem_mut = elem.borrow_mut();
38 elem_mut.property_declarations.insert(
39 prop_name.clone(),
40 PropertyDeclaration {
41 property_type: function_ty,
42 visibility: crate::object_tree::PropertyVisibility::Private,
43 pure: Some(true),
44 ..Default::default()
45 },
46 );
47 elem_mut.bindings.insert(prop_name, BindingExpression::new_with_span(body, span).into());
48 elem_mut.layout_info_v_with_constraint = Some(nr);
49}
50
51fn rewrite_layoutinfo_v_for_constraint(expr: &mut Expression, width_param: &Expression) {
55 expr.visit_recursive_mut(&mut |sub| match sub {
56 Expression::ComputeBoxLayoutInfo {
57 orientation: Orientation::Vertical,
58 cross_axis_size,
59 ..
60 }
61 | Expression::ComputeGridLayoutInfo {
62 orientation: Orientation::Vertical,
63 cross_axis_size,
64 ..
65 }
66 | Expression::ComputeFlexboxLayoutInfo {
67 orientation: Orientation::Vertical,
68 cross_axis_size,
69 ..
70 } => {
71 *cross_axis_size = Some(Box::new(width_param.clone()));
72 }
73 Expression::FunctionCall {
74 function: Callable::Builtin(BuiltinFunction::ImplicitLayoutInfo(Orientation::Vertical)),
75 arguments,
76 ..
77 } => {
78 let target = match arguments.first() {
80 Some(Expression::ElementReference(weak)) => weak.upgrade(),
81 _ => None,
82 };
83 if let Some(target) = target {
84 if let Some(constrained_nr) =
86 target.borrow().inherited_layout_info_v_with_constraint()
87 {
88 *sub = Expression::FunctionCall {
89 function: Callable::Function(crate::namedreference::NamedReference::new(
90 &target,
91 constrained_nr.name().clone(),
92 )),
93 arguments: vec![width_param.clone()],
94 source_location: None,
95 };
96 return;
97 }
98 if target.borrow().is_builtin_height_for_width() {
102 debug_assert!(arguments.len() >= 2);
103 if let Some(second) = arguments.get_mut(1) {
104 *second = width_param.clone();
105 }
106 }
107 }
108 }
109 Expression::PropertyReference(nr) => {
110 let target = nr.element();
113 let is_vertical_layout_info = target
114 .borrow()
115 .layout_info_prop(Orientation::Vertical)
116 .map(|prop_nr| {
117 prop_nr.name() == nr.name() && Rc::ptr_eq(&prop_nr.element(), &target)
118 })
119 .unwrap_or(false);
120 if !is_vertical_layout_info {
121 return;
122 }
123 if let Some(constrained_nr) = target.borrow().inherited_layout_info_v_with_constraint()
124 {
125 *sub = Expression::FunctionCall {
126 function: Callable::Function(crate::namedreference::NamedReference::new(
127 &target,
128 constrained_nr.name().clone(),
129 )),
130 arguments: vec![width_param.clone()],
131 source_location: None,
132 };
133 }
134 }
135 _ => {}
136 });
137}
138
139fn synthesize_layoutinfo_h_with_constraint_on(
141 elem: &ElementRc,
142 span: crate::diagnostics::SourceLocation,
143 body: Expression,
144) {
145 let function_ty = Type::Function(Rc::new(crate::langtype::Function {
146 return_type: crate::typeregister::layout_info_type().into(),
147 args: vec![Type::LogicalLength],
148 arg_names: vec![SmolStr::new_static("height")],
149 }));
150 let prop_name = SmolStr::new_static("layoutinfo-h-with-constraint");
151 let nr = crate::namedreference::NamedReference::new(elem, prop_name.clone());
152
153 let mut elem_mut = elem.borrow_mut();
154 elem_mut.property_declarations.insert(
155 prop_name.clone(),
156 PropertyDeclaration {
157 property_type: function_ty,
158 visibility: crate::object_tree::PropertyVisibility::Private,
159 pure: Some(true),
160 ..Default::default()
161 },
162 );
163 elem_mut.bindings.insert(prop_name, BindingExpression::new_with_span(body, span).into());
164 elem_mut.layout_info_h_with_constraint = Some(nr);
165}
166
167fn rewrite_layoutinfo_h_for_constraint(expr: &mut Expression, height_param: &Expression) {
173 expr.visit_recursive_mut(&mut |sub| match sub {
174 Expression::ComputeFlexboxLayoutInfo {
175 orientation: Orientation::Horizontal,
176 cross_axis_size,
177 ..
178 } => {
179 *cross_axis_size = Some(Box::new(height_param.clone()));
180 }
181 Expression::PropertyReference(nr) => {
182 let target = nr.element();
185 let is_horizontal_layout_info = target
186 .borrow()
187 .layout_info_prop(Orientation::Horizontal)
188 .map(|prop_nr| {
189 prop_nr.name() == nr.name() && Rc::ptr_eq(&prop_nr.element(), &target)
190 })
191 .unwrap_or(false);
192 if !is_horizontal_layout_info {
193 return;
194 }
195 if let Some(constrained_nr) = target.borrow().inherited_layout_info_h_with_constraint()
196 {
197 *sub = Expression::FunctionCall {
198 function: Callable::Function(crate::namedreference::NamedReference::new(
199 &target,
200 constrained_nr.name().clone(),
201 )),
202 arguments: vec![height_param.clone()],
203 source_location: None,
204 };
205 }
206 }
207 _ => {}
208 });
209}
210
211pub fn synthesize_layoutinfo_h_with_constraint(component: &Rc<Component>) {
216 fn walk(elem: &ElementRc) -> bool {
220 let children = elem.borrow().children.clone();
221 let mut has_h_cross = false;
222 for c in &children {
223 has_h_cross |= walk(c);
224 }
225 let repeated_body = {
228 let elem_b = elem.borrow();
229 if elem_b.repeated.is_some() {
230 if let ElementType::Component(base_comp) = &elem_b.base_type {
231 Some(base_comp.root_element.clone())
232 } else {
233 None
234 }
235 } else {
236 None
237 }
238 };
239 if let Some(body_root) = repeated_body {
240 has_h_cross |= walk(&body_root);
241 }
242
243 let (already_synthesized, base_has_constraint, self_is_h_cross_flex, h_nr_clone) = {
244 let elem_b = elem.borrow();
245 let layout_type = elem_b.debug.first().and_then(|d| d.layout.as_ref()).cloned();
246 let self_is = matches!(
247 layout_type,
248 Some(crate::layout::Layout::FlexboxLayout(ref l))
249 if !matches!(
250 l.axis_relation(Orientation::Horizontal),
251 crate::layout::FlexboxAxisRelation::MainAxis,
252 )
253 );
254 let base_has = matches!(
255 &elem_b.base_type,
256 ElementType::Component(base_comp)
257 if base_comp.root_element.borrow().layout_info_h_with_constraint.is_some()
258 );
259 (
260 elem_b.layout_info_h_with_constraint.is_some(),
261 base_has,
262 self_is,
263 elem_b.layout_info_prop(Orientation::Horizontal).cloned(),
264 )
265 };
266 has_h_cross |= self_is_h_cross_flex | base_has_constraint;
267
268 if !has_h_cross || already_synthesized {
269 return has_h_cross;
270 }
271 let Some(h_nr) = h_nr_clone else { return has_h_cross };
272 let Some(h_binding) = elem.borrow().bindings.get(h_nr.name()).map(|b| b.borrow().clone())
277 else {
278 return has_h_cross;
279 };
280
281 let span = h_binding.span.clone().unwrap_or_else(|| elem.borrow().to_source_location());
282 let mut body = h_binding.expression.clone();
283 let height_param =
284 Expression::FunctionParameterReference { index: 0, ty: Type::LogicalLength };
285 rewrite_layoutinfo_h_for_constraint(&mut body, &height_param);
286
287 synthesize_layoutinfo_h_with_constraint_on(elem, span, body);
288 has_h_cross
289 }
290 walk(&component.root_element);
291}
292
293pub fn synthesize_layoutinfo_v_with_constraint(component: &Rc<Component>) {
298 fn walk(elem: &ElementRc) -> bool {
302 let children = elem.borrow().children.clone();
303 let mut has_v_cross = false;
304 for c in &children {
305 has_v_cross |= walk(c);
306 }
307 let repeated_body = {
309 let elem_b = elem.borrow();
310 if elem_b.repeated.is_some() {
311 if let ElementType::Component(base_comp) = &elem_b.base_type {
312 Some(base_comp.root_element.clone())
313 } else {
314 None
315 }
316 } else {
317 None
318 }
319 };
320 if let Some(body_root) = repeated_body {
321 has_v_cross |= walk(&body_root);
322 }
323
324 let (already_synthesized, base_has_constraint, self_is_v_cross_flex, v_nr_clone) = {
325 let elem_b = elem.borrow();
326 has_v_cross |= elem_b.is_builtin_height_for_width();
327 let layout_type = elem_b.debug.first().and_then(|d| d.layout.as_ref()).cloned();
328 let self_is = matches!(
329 layout_type,
330 Some(crate::layout::Layout::FlexboxLayout(ref l))
331 if !matches!(
332 l.axis_relation(Orientation::Vertical),
333 crate::layout::FlexboxAxisRelation::MainAxis,
334 )
335 );
336 let base_has = matches!(
337 &elem_b.base_type,
338 ElementType::Component(base_comp)
339 if base_comp.root_element.borrow().layout_info_v_with_constraint.is_some()
340 );
341 (
342 elem_b.layout_info_v_with_constraint.is_some(),
343 base_has,
344 self_is,
345 elem_b.layout_info_prop(Orientation::Vertical).cloned(),
346 )
347 };
348 has_v_cross |= self_is_v_cross_flex | base_has_constraint;
349
350 if !has_v_cross || already_synthesized {
351 return has_v_cross;
352 }
353 let Some(v_nr) = v_nr_clone else { return has_v_cross };
354 let Some(v_binding) = elem.borrow().bindings.get(v_nr.name()).map(|b| b.borrow().clone())
355 else {
356 return has_v_cross;
357 };
358
359 let span = v_binding.span.clone().unwrap_or_else(|| elem.borrow().to_source_location());
360 let mut body = v_binding.expression.clone();
361 let width_param =
362 Expression::FunctionParameterReference { index: 0, ty: Type::LogicalLength };
363 rewrite_layoutinfo_v_for_constraint(&mut body, &width_param);
364
365 synthesize_layoutinfo_v_with_constraint_on(elem, span, body);
366 has_v_cross
367 }
368 walk(&component.root_element);
369}
370
371pub fn lower_layouts(
373 component: &Rc<Component>,
374 type_loader: &mut TypeLoader,
375 style_metrics: &Rc<Component>,
376 diag: &mut BuildDiagnostics,
377) {
378 recurse_elem_including_sub_components(component, &(), &mut |elem, _| {
380 if check_preferred_size_100(elem, "preferred-width", diag) {
381 elem.borrow_mut().default_fill_parent.0 = true;
382 }
383 if check_preferred_size_100(elem, "preferred-height", diag) {
384 elem.borrow_mut().default_fill_parent.1 = true;
385 }
386 let base = elem.borrow().sub_component().cloned();
387 if let Some(base) = base {
388 let base = base.root_element.borrow();
389 let mut elem_mut = elem.borrow_mut();
390 elem_mut.default_fill_parent.0 |= base.default_fill_parent.0;
391 elem_mut.default_fill_parent.1 |= base.default_fill_parent.1;
392 }
393 });
394
395 *component.root_constraints.borrow_mut() =
396 LayoutConstraints::new(&component.root_element, Some((diag, DiagnosticLevel::Error)));
397
398 recurse_elem_including_sub_components(
399 component,
400 &Option::default(),
401 &mut |elem, parent_layout_type| {
402 let component = elem.borrow().enclosing_component.upgrade().unwrap();
403
404 if Rc::ptr_eq(elem, &component.root_element) {
409 for popup in component.popup_windows.borrow().iter() {
410 *popup.component.root_constraints.borrow_mut() = LayoutConstraints::new(
411 &popup.component.root_element,
412 Some((&mut *diag, DiagnosticLevel::Warning)),
413 );
414 }
415 }
416
417 lower_element_layout(
418 &component,
419 elem,
420 &type_loader.global_type_registry.borrow(),
421 style_metrics,
422 parent_layout_type,
423 diag,
424 )
425 },
426 );
427}
428
429fn check_preferred_size_100(elem: &ElementRc, prop: &str, diag: &mut BuildDiagnostics) -> bool {
430 let ret = if let Some(p) = elem.borrow().bindings.get(prop) {
431 if p.borrow().expression.ty() == Type::Percent {
432 if !matches!(p.borrow().expression.ignore_debug_hooks(), Expression::NumberLiteral(val, _) if *val == 100.)
433 {
434 diag.push_error(
435 format!("{prop} must either be a length, or the literal '100%'"),
436 &*p.borrow(),
437 );
438 }
439 true
440 } else {
441 false
442 }
443 } else {
444 false
445 };
446 if ret {
447 elem.borrow_mut().bindings.remove(prop).unwrap();
448 return true;
449 }
450 false
451}
452
453fn lower_element_layout(
456 component: &Rc<Component>,
457 elem: &ElementRc,
458 type_register: &TypeRegister,
459 style_metrics: &Rc<Component>,
460 parent_layout_type: &Option<SmolStr>,
461 diag: &mut BuildDiagnostics,
462) -> Option<SmolStr> {
463 let layout_type = if let ElementType::Builtin(base_type) = &elem.borrow().base_type {
464 Some(base_type.name.clone())
465 } else {
466 None
467 };
468
469 check_no_layout_properties(elem, &layout_type, parent_layout_type, diag);
470
471 match layout_type.as_ref()?.as_str() {
472 "Row" => return layout_type,
473 "GridLayout" => lower_grid_layout(component, elem, diag, type_register),
474 "HorizontalLayout" => lower_box_layout(elem, diag, Orientation::Horizontal),
475 "VerticalLayout" => lower_box_layout(elem, diag, Orientation::Vertical),
476 "FlexboxLayout" => lower_flexbox_layout(elem, diag),
477 "Dialog" => {
478 lower_dialog_layout(elem, style_metrics, diag);
479 return layout_type;
481 }
482 _ => return None,
483 };
484
485 let mut elem = elem.borrow_mut();
486 let elem = &mut *elem;
487 let prev_base = std::mem::replace(&mut elem.base_type, type_register.empty_type());
488 elem.default_fill_parent = (true, true);
489 for (p, ty) in prev_base.property_list() {
492 if !elem.base_type.lookup_property(&p).is_valid()
493 && !elem.property_declarations.contains_key(&p)
494 {
495 elem.property_declarations.insert(p, ty.into());
496 }
497 }
498
499 layout_type
500}
501
502#[derive(Debug, PartialEq, Eq, Clone, Copy)]
504enum RowColExpressionType {
505 Auto, Literal,
507 RuntimeExpression,
508}
509impl RowColExpressionType {
510 fn from_option_expr(
511 expr: &Option<Expression>,
512 is_number_literal: bool,
513 ) -> RowColExpressionType {
514 match expr {
515 None => RowColExpressionType::Auto,
516 Some(_) if is_number_literal => RowColExpressionType::Literal,
517 Some(_) => RowColExpressionType::RuntimeExpression,
518 }
519 }
520}
521
522#[derive(Default)]
527struct NumberingTypes {
528 strict: Option<RowColExpressionType>,
529 lenient: Option<RowColExpressionType>,
530}
531
532fn lower_grid_layout(
533 component: &Rc<Component>,
534 grid_layout_element: &ElementRc,
535 diag: &mut BuildDiagnostics,
536 type_register: &TypeRegister,
537) {
538 let mut grid = GridLayout {
539 elems: Default::default(),
540 geometry: LayoutGeometry::new(grid_layout_element),
541 dialog_button_roles: None,
542 uses_auto: false,
543 };
544
545 let layout_organized_data_prop = create_new_prop(
546 grid_layout_element,
547 SmolStr::new_static("layout-organized-data"),
548 Type::ArrayOfU16,
549 );
550 let layout_cache_prop_h = create_new_prop(
551 grid_layout_element,
552 SmolStr::new_static("layout-cache-h"),
553 Type::LayoutCache,
554 );
555 let layout_cache_prop_v = create_new_prop(
556 grid_layout_element,
557 SmolStr::new_static("layout-cache-v"),
558 Type::LayoutCache,
559 );
560 let layout_info_prop_h = create_new_prop(
561 grid_layout_element,
562 SmolStr::new_static("layoutinfo-h"),
563 layout_info_type().into(),
564 );
565 let layout_info_prop_v = create_new_prop(
566 grid_layout_element,
567 SmolStr::new_static("layoutinfo-v"),
568 layout_info_type().into(),
569 );
570
571 let layout_children = std::mem::take(&mut grid_layout_element.borrow_mut().children);
572 let mut collected_children = Vec::new();
573 let mut new_row = false; let mut numbering_type = NumberingTypes::default();
592 let mut num_cached_items: usize = 0;
593 for layout_child in layout_children {
594 let is_repeated_row = {
595 if layout_child.borrow().repeated.is_some()
596 && let ElementType::Component(comp) = &layout_child.borrow().base_type
597 {
598 match &comp.root_element.borrow().base_type {
599 ElementType::Builtin(b) => b.name == "Row",
600 _ => false,
601 }
602 } else {
603 false
604 }
605 };
606 if is_repeated_row {
607 grid.add_repeated_row(
608 &layout_child,
609 &layout_cache_prop_h,
610 &layout_cache_prop_v,
611 &layout_organized_data_prop,
612 diag,
613 &mut num_cached_items,
614 );
615 collected_children.push(layout_child);
616 new_row = true;
617 } else if layout_child.borrow().base_type.type_name() == Some("Row") {
618 new_row = true;
619 let row_children = std::mem::take(&mut layout_child.borrow_mut().children);
620 for row_child in row_children {
621 if let Some(binding) = row_child.borrow_mut().bindings.get("row") {
622 diag.push_warning(
623 "The 'row' property cannot be used for elements inside a Row. This was accepted by previous versions of Slint, but may become an error in the future".to_string(),
624 &*binding.borrow(),
625 );
626 }
627 grid.add_element(
628 &row_child,
629 new_row,
630 &layout_cache_prop_h,
631 &layout_cache_prop_v,
632 &layout_organized_data_prop,
633 &mut numbering_type,
634 diag,
635 &mut num_cached_items,
636 );
637 collected_children.push(row_child);
638 new_row = false;
639 }
640 new_row = true; if layout_child.borrow().has_popup_child {
642 layout_child.borrow_mut().base_type = type_register.empty_type();
644 collected_children.push(layout_child);
645 } else {
646 component.optimized_elements.borrow_mut().push(layout_child);
647 }
648 } else {
649 grid.add_element(
650 &layout_child,
651 new_row,
652 &layout_cache_prop_h,
653 &layout_cache_prop_v,
654 &layout_organized_data_prop,
655 &mut numbering_type,
656 diag,
657 &mut num_cached_items,
658 );
659 collected_children.push(layout_child);
660 new_row = false;
661 }
662 }
663 grid_layout_element.borrow_mut().children = collected_children;
664 grid.uses_auto = numbering_type.strict == Some(RowColExpressionType::Auto);
665 let span = grid_layout_element.borrow().to_source_location();
666
667 layout_organized_data_prop.element().borrow_mut().bindings.insert(
668 layout_organized_data_prop.name().clone(),
669 BindingExpression::new_with_span(
670 Expression::OrganizeGridLayout(grid.clone()),
671 span.clone(),
672 )
673 .into(),
674 );
675 layout_cache_prop_h.element().borrow_mut().bindings.insert(
676 layout_cache_prop_h.name().clone(),
677 BindingExpression::new_with_span(
678 Expression::SolveGridLayout {
679 layout_organized_data_prop: layout_organized_data_prop.clone(),
680 layout: grid.clone(),
681 orientation: Orientation::Horizontal,
682 },
683 span.clone(),
684 )
685 .into(),
686 );
687 layout_cache_prop_v.element().borrow_mut().bindings.insert(
688 layout_cache_prop_v.name().clone(),
689 BindingExpression::new_with_span(
690 Expression::SolveGridLayout {
691 layout_organized_data_prop: layout_organized_data_prop.clone(),
692 layout: grid.clone(),
693 orientation: Orientation::Vertical,
694 },
695 span.clone(),
696 )
697 .into(),
698 );
699 layout_info_prop_h.element().borrow_mut().bindings.insert(
700 layout_info_prop_h.name().clone(),
701 BindingExpression::new_with_span(
702 Expression::ComputeGridLayoutInfo {
703 layout_organized_data_prop: layout_organized_data_prop.clone(),
704 layout: grid.clone(),
705 orientation: Orientation::Horizontal,
706 cross_axis_size: None,
707 },
708 span.clone(),
709 )
710 .into(),
711 );
712 layout_info_prop_v.element().borrow_mut().bindings.insert(
713 layout_info_prop_v.name().clone(),
714 BindingExpression::new_with_span(
715 Expression::ComputeGridLayoutInfo {
716 layout_organized_data_prop: layout_organized_data_prop.clone(),
717 layout: grid.clone(),
718 orientation: Orientation::Vertical,
719 cross_axis_size: None,
720 },
721 span,
722 )
723 .into(),
724 );
725 grid_layout_element.borrow_mut().layout_info_prop =
726 Some((layout_info_prop_h, layout_info_prop_v));
727 for d in grid_layout_element.borrow_mut().debug.iter_mut() {
728 d.layout = Some(Layout::GridLayout(grid.clone()));
729 }
730}
731
732impl GridLayout {
733 fn add_element(
734 &mut self,
735 item_element: &ElementRc,
736 new_row: bool,
737 layout_cache_prop_h: &NamedReference,
738 layout_cache_prop_v: &NamedReference,
739 organized_data_prop: &NamedReference,
740 numbering_type: &mut NumberingTypes,
741 diag: &mut BuildDiagnostics,
742 num_cached_items: &mut usize,
743 ) {
744 {
746 let mut check_expr = |name: &str| {
766 let mut is_number_literal = false;
767 let mut read = |elem: &ElementRc, lit: &mut bool| -> Option<Expression> {
768 let b = elem.borrow().bindings.get(name).cloned()?;
769 let b_borrow = b.borrow();
770 if !b_borrow.has_binding() {
771 return None;
772 }
773 *lit = check_number_literal_is_positive_integer(
774 &b_borrow.expression,
775 name,
776 &*b_borrow,
777 diag,
778 );
779 Some(b_borrow.expression.clone())
780 };
781 let lenient = read(item_element, &mut is_number_literal);
782 let strict = if lenient.is_some() {
783 lenient.clone()
784 } else if item_element.borrow().repeated.is_some()
785 && let ElementType::Component(base) = item_element.borrow().base_type.clone()
786 {
787 read(&base.root_element, &mut is_number_literal)
788 } else {
789 None
790 };
791 (strict, lenient, is_number_literal)
792 };
793
794 let (row_strict, row_lenient, row_lit) = check_expr("row");
795 let (col_strict, col_lenient, col_lit) = check_expr("col");
796 check_expr("rowspan");
797 check_expr("colspan");
798
799 let would_conflict = |num: &Option<RowColExpressionType>,
804 ty: &RowColExpressionType|
805 -> bool {
806 !matches!(ty, RowColExpressionType::Literal) && matches!(num, Some(t) if t != ty)
807 };
808
809 let mut classify_and_update =
814 |strict: RowColExpressionType, lenient: RowColExpressionType| -> Option<bool> {
815 let diag = if would_conflict(&numbering_type.strict, &strict) {
816 Some(would_conflict(&numbering_type.lenient, &lenient))
820 } else {
821 None
822 };
823 if numbering_type.strict.is_none()
827 && !matches!(strict, RowColExpressionType::Literal)
828 {
829 numbering_type.strict = Some(strict);
830 }
831 if numbering_type.lenient.is_none()
832 && !matches!(lenient, RowColExpressionType::Literal)
833 {
834 numbering_type.lenient = Some(lenient);
835 }
836 diag
837 };
838
839 let row_strict_ty = RowColExpressionType::from_option_expr(&row_strict, row_lit);
840 let row_lenient_ty = RowColExpressionType::from_option_expr(&row_lenient, row_lit);
841 let col_strict_ty = RowColExpressionType::from_option_expr(&col_strict, col_lit);
842 let col_lenient_ty = RowColExpressionType::from_option_expr(&col_lenient, col_lit);
843
844 let row_diag = classify_and_update(row_strict_ty, row_lenient_ty);
845 let col_diag = classify_and_update(col_strict_ty, col_lenient_ty);
846
847 let report = match (row_diag, col_diag) {
851 (Some(true), _) => Some(("row", true)),
852 (_, Some(true)) => Some(("col", true)),
853 (Some(false), _) => Some(("row", false)),
854 (_, Some(false)) => Some(("col", false)),
855 _ => None,
856 };
857
858 if let Some((prop_name, is_error)) = report {
859 let element_ref = item_element.borrow();
864 let inner_borrow = match &element_ref.base_type {
865 ElementType::Component(base) if element_ref.repeated.is_some() => {
866 Some(base.root_element.clone())
867 }
868 _ => None,
869 };
870 let direct_binding = element_ref.bindings.get(prop_name).cloned();
871 let inner_binding =
872 inner_borrow.as_ref().and_then(|e| e.borrow().bindings.get(prop_name).cloned());
873 let binding = direct_binding.or(inner_binding);
874 let binding_borrow = binding.as_ref().map(|b| b.borrow());
875 let span: &dyn Spanned = match &binding_borrow {
876 Some(b) => &**b,
877 None => &*element_ref,
878 };
879 if is_error {
880 diag.push_error(
881 format!("Cannot mix auto-numbering and runtime expressions for the '{prop_name}' property"),
882 span,
883 );
884 } else {
885 diag.push_warning(
886 format!("Cannot mix auto-numbering and runtime expressions for the '{prop_name}' property. This was accepted by previous versions of Slint, but may become an error in the future"),
887 span,
888 );
889 }
890 }
891 }
892
893 let propref = |name: &'static str| -> Option<RowColExpr> {
894 let nr = crate::layout::binding_reference(item_element, name).map(|nr| {
895 let e = nr.element();
897 let mut nr = nr.clone();
898 if e.borrow().repeated.is_some()
899 && let crate::langtype::ElementType::Component(c) = e.borrow().base_type.clone()
900 {
901 nr = NamedReference::new(&c.root_element, nr.name().clone())
902 };
903 nr
904 });
905 nr.map(RowColExpr::Named)
906 };
907
908 let row_expr = propref("row");
909 let col_expr = propref("col");
910 let rowspan_expr = propref("rowspan");
911 let colspan_expr = propref("colspan");
912
913 self.add_element_with_coord_as_expr(
914 item_element,
915 new_row,
916 (&row_expr, &col_expr),
917 (&rowspan_expr, &colspan_expr),
918 layout_cache_prop_h,
919 layout_cache_prop_v,
920 organized_data_prop,
921 diag,
922 num_cached_items,
923 );
924 }
925
926 fn add_element_with_coord(
927 &mut self,
928 item_element: &ElementRc,
929 (row, col): (u16, u16),
930 (rowspan, colspan): (u16, u16),
931 layout_cache_prop_h: &NamedReference,
932 layout_cache_prop_v: &NamedReference,
933 organized_data_prop: &NamedReference,
934 diag: &mut BuildDiagnostics,
935 num_cached_items: &mut usize,
936 ) {
937 self.add_element_with_coord_as_expr(
938 item_element,
939 false, (&Some(RowColExpr::Literal(row)), &Some(RowColExpr::Literal(col))),
941 (&Some(RowColExpr::Literal(rowspan)), &Some(RowColExpr::Literal(colspan))),
942 layout_cache_prop_h,
943 layout_cache_prop_v,
944 organized_data_prop,
945 diag,
946 num_cached_items,
947 )
948 }
949
950 fn add_repeated_row(
951 &mut self,
952 item_element: &ElementRc,
953 layout_cache_prop_h: &NamedReference,
954 layout_cache_prop_v: &NamedReference,
955 organized_data_prop: &NamedReference,
956 diag: &mut BuildDiagnostics,
957 num_cached_items: &mut usize,
958 ) {
959 let layout_item = create_layout_item(item_element, diag);
960 if let ElementType::Component(comp) = &item_element.borrow().base_type {
961 let mut children_layout_items = Vec::new();
962 let jump_pos = *num_cached_items;
963
964 let children_ref = comp.root_element.borrow().children.clone();
966 let has_inner_repeaters = children_ref.iter().any(|c| c.borrow().repeated.is_some());
967
968 let step = children_ref.len() as f64;
973 let (stride_h_expr, stride_v_expr, stride_org_expr): (
974 Expression,
975 Expression,
976 Expression,
977 ) = if has_inner_repeaters {
978 (
981 Expression::LayoutCacheAccess {
982 layout_cache_prop: layout_cache_prop_h.clone(),
983 index: jump_pos * 2 + 1,
984 repeater_index: None,
985 entries_per_item: 1,
986 },
987 Expression::LayoutCacheAccess {
988 layout_cache_prop: layout_cache_prop_v.clone(),
989 index: jump_pos * 2 + 1,
990 repeater_index: None,
991 entries_per_item: 1,
992 },
993 Expression::LayoutCacheAccess {
994 layout_cache_prop: organized_data_prop.clone(),
995 index: jump_pos * 4 + 1,
996 repeater_index: None,
997 entries_per_item: 1,
998 },
999 )
1000 } else {
1001 (
1003 Expression::NumberLiteral(step * 2.0, Unit::None), Expression::NumberLiteral(step * 2.0, Unit::None), Expression::NumberLiteral(step * 4.0, Unit::None), )
1007 };
1008
1009 let mut cumulative_pos: Option<Expression> = None;
1020
1021 for child in children_ref.iter() {
1022 let is_nested_repeater = child.borrow().repeated.is_some();
1023 let sub_item = create_layout_item(child, diag);
1024
1025 let propref = |name: &'static str, elem: &ElementRc| -> Option<RowColExpr> {
1027 let nr = crate::layout::binding_reference(elem, name).map(|nr| {
1028 let e = nr.element();
1029 let mut nr = nr.clone();
1030 if e.borrow().repeated.is_some()
1031 && let crate::langtype::ElementType::Component(c) =
1032 e.borrow().base_type.clone()
1033 {
1034 nr = NamedReference::new(&c.root_element, nr.name().clone())
1035 };
1036 nr
1037 });
1038 nr.map(RowColExpr::Named)
1039 };
1040 let colspan_expr = propref("colspan", child);
1041 let rowspan_expr = propref("rowspan", child);
1042 let child_grid_cell = Rc::new(RefCell::new(GridLayoutCell {
1043 new_row: false,
1044 col_expr: RowColExpr::Auto,
1045 row_expr: RowColExpr::Auto,
1046 colspan_expr: colspan_expr.unwrap_or(RowColExpr::Literal(1)),
1047 rowspan_expr: rowspan_expr.unwrap_or(RowColExpr::Literal(1)),
1048 child_items: None,
1049 }));
1050 child.borrow_mut().grid_layout_cell = Some(child_grid_cell);
1051
1052 let effective_inner_rep_idx = if is_nested_repeater {
1058 let model_idx = sub_item.repeater_index.clone().unwrap();
1060 Some(if let Some(ref base) = cumulative_pos {
1061 Expression::BinaryExpression {
1062 lhs: Box::new(base.clone()),
1063 rhs: Box::new(model_idx),
1064 op: '+',
1065 }
1066 } else {
1067 model_idx
1068 })
1069 } else {
1070 cumulative_pos.clone()
1072 };
1073
1074 let repeater_params = RepeaterCacheParams {
1075 index: jump_pos,
1076 rep_idx: &layout_item.repeater_index,
1077 child_offset: 0,
1078 inner_rep_idx: &effective_inner_rep_idx,
1079 };
1080 set_coord_prop_from_cache(
1082 &sub_item.elem,
1083 &sub_item.item.constraints,
1084 layout_cache_prop_h,
1085 layout_cache_prop_v,
1086 &repeater_params,
1087 Some(&stride_h_expr),
1088 Some(&stride_v_expr),
1089 diag,
1090 );
1091 set_grid_rowcol_from_cache(
1093 &sub_item.elem,
1094 organized_data_prop,
1095 &repeater_params,
1096 Some(&stride_org_expr),
1097 (&None::<RowColExpr>, &None::<RowColExpr>),
1098 diag,
1099 );
1100
1101 if is_nested_repeater {
1103 let (model_expr, is_conditional) = {
1107 let b = child.borrow();
1108 let r = b.repeated.as_ref().unwrap();
1109 (r.model.clone(), r.is_conditional_element)
1110 };
1111 let len_expr = if is_conditional {
1112 Expression::Condition {
1113 condition: Box::new(model_expr),
1114 true_expr: Box::new(Expression::NumberLiteral(1., Unit::None)),
1115 false_expr: Box::new(Expression::NumberLiteral(0., Unit::None)),
1116 }
1117 } else {
1118 Expression::FunctionCall {
1119 function: Callable::Builtin(BuiltinFunction::ArrayLength),
1120 arguments: vec![model_expr],
1121 source_location: None,
1122 }
1123 };
1124 cumulative_pos = Some(if let Some(prev) = cumulative_pos.take() {
1125 Expression::BinaryExpression {
1126 lhs: Box::new(prev),
1127 rhs: Box::new(len_expr),
1128 op: '+',
1129 }
1130 } else {
1131 len_expr
1132 });
1133 } else {
1134 cumulative_pos = Some(if let Some(prev) = cumulative_pos.take() {
1136 Expression::BinaryExpression {
1137 lhs: Box::new(prev),
1138 rhs: Box::new(Expression::NumberLiteral(1., Unit::None)),
1139 op: '+',
1140 }
1141 } else {
1142 Expression::NumberLiteral(1., Unit::None)
1143 });
1144 }
1145
1146 if is_nested_repeater {
1147 children_layout_items.push(RowChildTemplate::Repeated {
1148 item: sub_item.item,
1149 repeated_element: child.clone(),
1150 });
1151 } else {
1152 children_layout_items.push(RowChildTemplate::Static(sub_item.item));
1153 }
1154 }
1155
1156 *num_cached_items += 1;
1158 let grid_layout_cell = Rc::new(RefCell::new(GridLayoutCell {
1160 new_row: true,
1161 col_expr: RowColExpr::Auto,
1162 row_expr: RowColExpr::Auto,
1163 colspan_expr: RowColExpr::Literal(1),
1164 rowspan_expr: RowColExpr::Literal(1),
1165 child_items: Some(children_layout_items),
1166 }));
1167 let grid_layout_element = GridLayoutElement {
1168 cell: grid_layout_cell.clone(),
1169 item: layout_item.item.clone(),
1170 };
1171 comp.root_element.borrow_mut().grid_layout_cell = Some(grid_layout_cell);
1172 self.elems.push(grid_layout_element);
1173 }
1174 }
1175
1176 fn add_element_with_coord_as_expr(
1177 &mut self,
1178 item_element: &ElementRc,
1179 new_row: bool,
1180 (row_expr, col_expr): (&Option<RowColExpr>, &Option<RowColExpr>),
1181 (rowspan_expr, colspan_expr): (&Option<RowColExpr>, &Option<RowColExpr>),
1182 layout_cache_prop_h: &NamedReference,
1183 layout_cache_prop_v: &NamedReference,
1184 organized_data_prop: &NamedReference,
1185 diag: &mut BuildDiagnostics,
1186 num_cached_items: &mut usize,
1187 ) {
1188 let layout_item = create_layout_item(item_element, diag);
1189
1190 let has_repeater_indirection = layout_item.repeater_index.is_some();
1191 let stride_coord =
1193 has_repeater_indirection.then(|| Expression::NumberLiteral(2.0, Unit::None));
1194 let stride_org =
1195 has_repeater_indirection.then(|| Expression::NumberLiteral(4.0, Unit::None));
1196 let repeater_params = RepeaterCacheParams {
1197 index: *num_cached_items,
1198 rep_idx: &layout_item.repeater_index,
1199 child_offset: 0,
1200 inner_rep_idx: &None,
1201 };
1202 set_coord_prop_from_cache(
1203 &layout_item.elem,
1204 &layout_item.item.constraints,
1205 layout_cache_prop_h,
1206 layout_cache_prop_v,
1207 &repeater_params,
1208 stride_coord.as_ref(),
1209 stride_coord.as_ref(),
1210 diag,
1211 );
1212 set_grid_rowcol_from_cache(
1213 &layout_item.elem,
1214 organized_data_prop,
1215 &repeater_params,
1216 stride_org.as_ref(),
1217 (row_expr, col_expr),
1218 diag,
1219 );
1220
1221 let expr_or_default = |expr: &Option<RowColExpr>, default: RowColExpr| -> RowColExpr {
1222 match expr {
1223 Some(RowColExpr::Literal(v)) => RowColExpr::Literal(*v),
1224 Some(RowColExpr::Named(nr)) => RowColExpr::Named(nr.clone()),
1225 Some(RowColExpr::Auto) => RowColExpr::Auto,
1226 None => default,
1227 }
1228 };
1229
1230 let grid_layout_cell = Rc::new(RefCell::new(GridLayoutCell {
1231 new_row,
1232 col_expr: expr_or_default(col_expr, RowColExpr::Auto),
1233 row_expr: expr_or_default(row_expr, RowColExpr::Auto),
1234 colspan_expr: expr_or_default(colspan_expr, RowColExpr::Literal(1)),
1235 rowspan_expr: expr_or_default(rowspan_expr, RowColExpr::Literal(1)),
1236 child_items: None,
1237 }));
1238 let grid_layout_element =
1239 GridLayoutElement { cell: grid_layout_cell.clone(), item: layout_item.item.clone() };
1240 layout_item.elem.borrow_mut().grid_layout_cell = Some(grid_layout_cell);
1241 self.elems.push(grid_layout_element);
1242 *num_cached_items += 1;
1243 }
1244}
1245
1246fn lower_box_layout(
1247 layout_element: &ElementRc,
1248 diag: &mut BuildDiagnostics,
1249 orientation: Orientation,
1250) {
1251 let mut layout = BoxLayout {
1252 orientation,
1253 elems: Default::default(),
1254 geometry: LayoutGeometry::new(layout_element),
1255 cross_alignment: binding_reference(layout_element, "cross-axis-alignment"),
1256 };
1257
1258 let layout_cache_prop =
1259 create_new_prop(layout_element, SmolStr::new_static("layout-cache"), Type::LayoutCache);
1260 let layout_cache_ortho_prop = layout.cross_alignment.is_some().then(|| {
1261 create_new_prop(
1262 layout_element,
1263 SmolStr::new_static("layout-cache-ortho"),
1264 Type::LayoutCache,
1265 )
1266 });
1267 let layout_info_prop_v = create_new_prop(
1268 layout_element,
1269 SmolStr::new_static("layoutinfo-v"),
1270 layout_info_type().into(),
1271 );
1272 let layout_info_prop_h = create_new_prop(
1273 layout_element,
1274 SmolStr::new_static("layoutinfo-h"),
1275 layout_info_type().into(),
1276 );
1277
1278 let layout_children = std::mem::take(&mut layout_element.borrow_mut().children);
1279
1280 let (pos, size, pad, ortho) = match orientation {
1281 Orientation::Horizontal => ("x", "width", "y", "height"),
1282 Orientation::Vertical => ("y", "height", "x", "width"),
1283 };
1284 let stretch_bindings = layout_cache_ortho_prop.is_none().then(|| {
1286 let (begin_padding, end_padding) = match orientation {
1287 Orientation::Horizontal => {
1288 (&layout.geometry.padding.top, &layout.geometry.padding.bottom)
1289 }
1290 Orientation::Vertical => {
1291 (&layout.geometry.padding.left, &layout.geometry.padding.right)
1292 }
1293 };
1294 let pad_expr = begin_padding.clone().map(Expression::PropertyReference);
1295 let mut size_expr = Expression::PropertyReference(NamedReference::new(
1296 layout_element,
1297 SmolStr::new_static(ortho),
1298 ));
1299 for p in [begin_padding, end_padding].into_iter().flatten() {
1300 size_expr = Expression::BinaryExpression {
1301 lhs: Box::new(std::mem::take(&mut size_expr)),
1302 rhs: Box::new(Expression::PropertyReference(p.clone())),
1303 op: '-',
1304 };
1305 }
1306 (pad_expr, size_expr)
1307 });
1308
1309 for layout_child in &layout_children {
1310 let item = create_layout_item(layout_child, diag);
1311 let index = layout.elems.len() * 2;
1312 let rep_idx = &item.repeater_index;
1313 let (fixed_size, fixed_ortho) = match orientation {
1314 Orientation::Horizontal => {
1315 (item.item.constraints.fixed_width, item.item.constraints.fixed_height)
1316 }
1317 Orientation::Vertical => {
1318 (item.item.constraints.fixed_height, item.item.constraints.fixed_width)
1319 }
1320 };
1321 let actual_elem = &item.elem;
1322 set_prop_from_cache(actual_elem, pos, &layout_cache_prop, index, rep_idx, 2, diag);
1323 if !fixed_size {
1324 set_prop_from_cache(actual_elem, size, &layout_cache_prop, index + 1, rep_idx, 2, diag);
1325 }
1326 if let Some(cache_ortho) = &layout_cache_ortho_prop {
1327 set_prop_from_cache(actual_elem, pad, cache_ortho, index, rep_idx, 2, diag);
1328 if !fixed_ortho {
1329 set_prop_from_cache(actual_elem, ortho, cache_ortho, index + 1, rep_idx, 2, diag);
1330 }
1331 } else {
1332 let (pad_expr, size_expr) = stretch_bindings.as_ref().unwrap();
1333 if let Some(pad_expr) = pad_expr {
1334 actual_elem
1335 .borrow_mut()
1336 .bindings
1337 .insert(pad.into(), RefCell::new(pad_expr.clone().into()));
1338 }
1339 if !fixed_ortho {
1340 actual_elem
1341 .borrow_mut()
1342 .bindings
1343 .insert(ortho.into(), RefCell::new(size_expr.clone().into()));
1344 }
1345 }
1346 layout.elems.push(item.item);
1347 }
1348 layout_element.borrow_mut().children = layout_children;
1349 let span = layout_element.borrow().to_source_location();
1350 layout_cache_prop.element().borrow_mut().bindings.insert(
1351 layout_cache_prop.name().clone(),
1352 BindingExpression::new_with_span(
1353 Expression::SolveBoxLayout(layout.clone(), orientation),
1354 span.clone(),
1355 )
1356 .into(),
1357 );
1358 if let Some(cache_ortho) = &layout_cache_ortho_prop {
1359 cache_ortho.element().borrow_mut().bindings.insert(
1360 cache_ortho.name().clone(),
1361 BindingExpression::new_with_span(
1362 Expression::SolveBoxLayout(layout.clone(), orientation.orthogonal()),
1363 span.clone(),
1364 )
1365 .into(),
1366 );
1367 }
1368 layout_info_prop_h.element().borrow_mut().bindings.insert(
1369 layout_info_prop_h.name().clone(),
1370 BindingExpression::new_with_span(
1371 Expression::ComputeBoxLayoutInfo {
1372 layout: layout.clone(),
1373 orientation: Orientation::Horizontal,
1374 cross_axis_size: None,
1375 },
1376 span.clone(),
1377 )
1378 .into(),
1379 );
1380 layout_info_prop_v.element().borrow_mut().bindings.insert(
1381 layout_info_prop_v.name().clone(),
1382 BindingExpression::new_with_span(
1383 Expression::ComputeBoxLayoutInfo {
1384 layout: layout.clone(),
1385 orientation: Orientation::Vertical,
1386 cross_axis_size: None,
1387 },
1388 span,
1389 )
1390 .into(),
1391 );
1392 layout_element.borrow_mut().layout_info_prop = Some((layout_info_prop_h, layout_info_prop_v));
1393 for d in layout_element.borrow_mut().debug.iter_mut() {
1394 d.layout = Some(Layout::BoxLayout(layout.clone()));
1395 }
1396}
1397
1398fn lower_flexbox_layout(layout_element: &ElementRc, diag: &mut BuildDiagnostics) {
1399 if let Some(binding) = layout_element.borrow().bindings.get("alignment") {
1402 let binding = binding.borrow();
1403 if matches!(binding.expression.ignore_debug_hooks(),
1404 Expression::EnumerationValue(v) if v.enumeration.name == "LayoutAlignment"
1405 && v.enumeration.values[v.value] == "stretch")
1406 {
1407 diag.push_warning(
1408 "alignment: stretch has no effect on FlexboxLayout".into(),
1409 &*binding,
1410 );
1411 }
1412 }
1413
1414 let direction = crate::layout::binding_reference(layout_element, "flex-direction");
1415 let align_content = crate::layout::binding_reference(layout_element, "align-content");
1416 let cross_axis_alignment =
1417 crate::layout::binding_reference(layout_element, "cross-axis-alignment");
1418 let flex_wrap = crate::layout::binding_reference(layout_element, "flex-wrap");
1419
1420 let mut layout = crate::layout::FlexboxLayout {
1421 elems: Default::default(),
1422 geometry: LayoutGeometry::new(layout_element),
1423 direction,
1424 align_content,
1425 cross_axis_alignment,
1426 flex_wrap,
1427 };
1428
1429 let layout_cache_prop =
1431 create_new_prop(layout_element, SmolStr::new_static("layout-cache"), Type::LayoutCache);
1432 let layout_info_prop_v = create_new_prop(
1433 layout_element,
1434 SmolStr::new_static("layoutinfo-v"),
1435 layout_info_type().into(),
1436 );
1437 let layout_info_prop_h = create_new_prop(
1438 layout_element,
1439 SmolStr::new_static("layoutinfo-h"),
1440 layout_info_type().into(),
1441 );
1442
1443 let layout_children = std::mem::take(&mut layout_element.borrow_mut().children);
1444
1445 for layout_child in &layout_children {
1446 let item = create_layout_item(layout_child, diag);
1447 let index = layout.elems.len() * 4; let rep_idx = &item.repeater_index;
1449 let actual_elem = &item.elem;
1450
1451 set_prop_from_cache(actual_elem, "x", &layout_cache_prop, index, rep_idx, 4, diag);
1453 set_prop_from_cache(actual_elem, "y", &layout_cache_prop, index + 1, rep_idx, 4, diag);
1455 if !item.item.constraints.fixed_width {
1457 set_prop_from_cache(
1458 actual_elem,
1459 "width",
1460 &layout_cache_prop,
1461 index + 2,
1462 rep_idx,
1463 4,
1464 diag,
1465 );
1466 }
1467 if !item.item.constraints.fixed_height {
1469 set_prop_from_cache(
1470 actual_elem,
1471 "height",
1472 &layout_cache_prop,
1473 index + 3,
1474 rep_idx,
1475 4,
1476 diag,
1477 );
1478 }
1479 let flex_grow = crate::layout::binding_reference(actual_elem, "flex-grow");
1480 let flex_shrink = crate::layout::binding_reference(actual_elem, "flex-shrink");
1481 let flex_basis = crate::layout::binding_reference(actual_elem, "flex-basis");
1482 let align_self = crate::layout::binding_reference(actual_elem, "flex-align-self");
1483 let order = crate::layout::binding_reference(actual_elem, "flex-order");
1484 layout.elems.push(crate::layout::FlexboxLayoutItem {
1485 item: item.item,
1486 flex_grow,
1487 flex_shrink,
1488 flex_basis,
1489 align_self,
1490 order,
1491 });
1492 }
1493 layout_element.borrow_mut().children = layout_children;
1494 let span = layout_element.borrow().to_source_location();
1495
1496 layout_cache_prop.element().borrow_mut().bindings.insert(
1497 layout_cache_prop.name().clone(),
1498 BindingExpression::new_with_span(
1499 Expression::SolveFlexboxLayout(layout.clone()),
1500 span.clone(),
1501 )
1502 .into(),
1503 );
1504 layout_info_prop_h.element().borrow_mut().bindings.insert(
1505 layout_info_prop_h.name().clone(),
1506 BindingExpression::new_with_span(
1507 Expression::ComputeFlexboxLayoutInfo {
1508 layout: layout.clone(),
1509 orientation: Orientation::Horizontal,
1510 cross_axis_size: None,
1511 },
1512 span.clone(),
1513 )
1514 .into(),
1515 );
1516 layout_info_prop_v.element().borrow_mut().bindings.insert(
1517 layout_info_prop_v.name().clone(),
1518 BindingExpression::new_with_span(
1519 Expression::ComputeFlexboxLayoutInfo {
1520 layout: layout.clone(),
1521 orientation: Orientation::Vertical,
1522 cross_axis_size: None,
1523 },
1524 span,
1525 )
1526 .into(),
1527 );
1528 layout_element.borrow_mut().layout_info_prop = Some((layout_info_prop_h, layout_info_prop_v));
1529 for d in layout_element.borrow_mut().debug.iter_mut() {
1530 d.layout = Some(Layout::FlexboxLayout(layout.clone()));
1531 }
1532}
1533
1534fn lower_dialog_layout(
1535 dialog_element: &ElementRc,
1536 style_metrics: &Rc<Component>,
1537 diag: &mut BuildDiagnostics,
1538) {
1539 let mut grid = GridLayout {
1540 elems: Default::default(),
1541 geometry: LayoutGeometry::new(dialog_element),
1542 dialog_button_roles: None,
1543 uses_auto: true,
1544 };
1545 let metrics = &style_metrics.root_element;
1546 grid.geometry
1547 .padding
1548 .bottom
1549 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-padding")));
1550 grid.geometry
1551 .padding
1552 .top
1553 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-padding")));
1554 grid.geometry
1555 .padding
1556 .left
1557 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-padding")));
1558 grid.geometry
1559 .padding
1560 .right
1561 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-padding")));
1562 grid.geometry
1563 .spacing
1564 .horizontal
1565 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-spacing")));
1566 grid.geometry
1567 .spacing
1568 .vertical
1569 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-spacing")));
1570
1571 let layout_organized_data_prop = create_new_prop(
1572 dialog_element,
1573 SmolStr::new_static("layout-organized-data"),
1574 Type::ArrayOfU16,
1575 );
1576 let layout_cache_prop_h =
1577 create_new_prop(dialog_element, SmolStr::new_static("layout-cache-h"), Type::LayoutCache);
1578 let layout_cache_prop_v =
1579 create_new_prop(dialog_element, SmolStr::new_static("layout-cache-v"), Type::LayoutCache);
1580 let layout_info_prop_h = create_new_prop(
1581 dialog_element,
1582 SmolStr::new_static("layoutinfo-h"),
1583 layout_info_type().into(),
1584 );
1585 let layout_info_prop_v = create_new_prop(
1586 dialog_element,
1587 SmolStr::new_static("layoutinfo-v"),
1588 layout_info_type().into(),
1589 );
1590
1591 let mut main_widget = None;
1592 let mut button_roles = Vec::new();
1593 let mut seen_buttons = HashSet::new();
1594 let mut num_cached_items: usize = 0;
1595 let layout_children = std::mem::take(&mut dialog_element.borrow_mut().children);
1596 for layout_child in &layout_children {
1597 let dialog_button_role_binding =
1598 layout_child.borrow_mut().bindings.remove("dialog-button-role");
1599 let is_button = if let Some(role_binding) = dialog_button_role_binding {
1600 let role_binding = role_binding.into_inner();
1601 if let Expression::EnumerationValue(val) =
1602 super::ignore_debug_hooks(&role_binding.expression)
1603 {
1604 let en = &val.enumeration;
1605 debug_assert_eq!(en.name, "DialogButtonRole");
1606 button_roles.push(en.values[val.value].clone());
1607 if val.value == 0 {
1608 diag.push_error(
1609 "The `dialog-button-role` cannot be set explicitly to none".into(),
1610 &role_binding,
1611 );
1612 }
1613 } else {
1614 diag.push_error(
1615 "The `dialog-button-role` property must be known at compile-time".into(),
1616 &role_binding,
1617 );
1618 }
1619 true
1620 } else if matches!(&layout_child.borrow().lookup_property("kind").property_type, Type::Enumeration(e) if e.name == "StandardButtonKind")
1621 {
1622 match layout_child.borrow().bindings.get("kind") {
1624 None => diag.push_error(
1625 "The `kind` property of the StandardButton in a Dialog must be set".into(),
1626 &*layout_child.borrow(),
1627 ),
1628 Some(binding) => {
1629 let binding = &*binding.borrow();
1630 if let Expression::EnumerationValue(val) =
1631 super::ignore_debug_hooks(&binding.expression)
1632 {
1633 let en = &val.enumeration;
1634 debug_assert_eq!(en.name, "StandardButtonKind");
1635 let kind = &en.values[val.value];
1636 let role = match kind.as_str() {
1637 "ok" => "accept",
1638 "cancel" => "reject",
1639 "apply" => "apply",
1640 "close" => "reject",
1641 "reset" => "reset",
1642 "help" => "help",
1643 "yes" => "accept",
1644 "no" => "reject",
1645 "abort" => "reject",
1646 "retry" => "accept",
1647 "ignore" => "accept",
1648 _ => unreachable!(),
1649 };
1650 button_roles.push(role.into());
1651 if !seen_buttons.insert(val.value) {
1652 diag.push_error("Duplicated `kind`: There are two StandardButton in this Dialog with the same kind".into(), binding);
1653 } else if Rc::ptr_eq(
1654 dialog_element,
1655 &dialog_element
1656 .borrow()
1657 .enclosing_component
1658 .upgrade()
1659 .unwrap()
1660 .root_element,
1661 ) {
1662 let clicked_ty =
1663 layout_child.borrow().lookup_property("clicked").property_type;
1664 if matches!(&clicked_ty, Type::Callback { .. })
1665 && layout_child.borrow().bindings.get("clicked").is_none_or(|c| {
1666 matches!(c.borrow().expression, Expression::Invalid)
1667 })
1668 {
1669 dialog_element
1670 .borrow_mut()
1671 .property_declarations
1672 .entry(format_smolstr!("{}-clicked", kind))
1673 .or_insert_with(|| PropertyDeclaration {
1674 property_type: clicked_ty,
1675 node: None,
1676 expose_in_public_api: true,
1677 is_alias: Some(NamedReference::new(
1678 layout_child,
1679 SmolStr::new_static("clicked"),
1680 )),
1681 visibility: PropertyVisibility::InOut,
1682 pure: None,
1683 shadows_builtin: false,
1684 });
1685 }
1686 }
1687 } else {
1688 diag.push_error(
1689 "The `kind` property of the StandardButton in a Dialog must be known at compile-time"
1690 .into(),
1691 binding,
1692 );
1693 }
1694 }
1695 }
1696 true
1697 } else {
1698 false
1699 };
1700
1701 if is_button {
1702 grid.add_element_with_coord(
1703 layout_child,
1704 (1, button_roles.len() as u16),
1705 (1, 1),
1706 &layout_cache_prop_h,
1707 &layout_cache_prop_v,
1708 &layout_organized_data_prop,
1709 diag,
1710 &mut num_cached_items,
1711 );
1712 } else if main_widget.is_some() {
1713 diag.push_error(
1714 "A Dialog can have only one child element that is not a StandardButton".into(),
1715 &*layout_child.borrow(),
1716 );
1717 } else {
1718 main_widget = Some(layout_child.clone())
1719 }
1720 }
1721 dialog_element.borrow_mut().children = layout_children;
1722
1723 if let Some(main_widget) = main_widget {
1724 grid.add_element_with_coord(
1725 &main_widget,
1726 (0, 0),
1727 (1, button_roles.len() as u16 + 1),
1728 &layout_cache_prop_h,
1729 &layout_cache_prop_v,
1730 &layout_organized_data_prop,
1731 diag,
1732 &mut num_cached_items,
1733 );
1734 } else {
1735 diag.push_error(
1736 "A Dialog must have a single child element that is not StandardButton".into(),
1737 &*dialog_element.borrow(),
1738 );
1739 }
1740 grid.dialog_button_roles = Some(button_roles);
1741
1742 let span = dialog_element.borrow().to_source_location();
1743 layout_organized_data_prop.element().borrow_mut().bindings.insert(
1744 layout_organized_data_prop.name().clone(),
1745 BindingExpression::new_with_span(
1746 Expression::OrganizeGridLayout(grid.clone()),
1747 span.clone(),
1748 )
1749 .into(),
1750 );
1751 layout_cache_prop_h.element().borrow_mut().bindings.insert(
1752 layout_cache_prop_h.name().clone(),
1753 BindingExpression::new_with_span(
1754 Expression::SolveGridLayout {
1755 layout_organized_data_prop: layout_organized_data_prop.clone(),
1756 layout: grid.clone(),
1757 orientation: Orientation::Horizontal,
1758 },
1759 span.clone(),
1760 )
1761 .into(),
1762 );
1763 layout_cache_prop_v.element().borrow_mut().bindings.insert(
1764 layout_cache_prop_v.name().clone(),
1765 BindingExpression::new_with_span(
1766 Expression::SolveGridLayout {
1767 layout_organized_data_prop: layout_organized_data_prop.clone(),
1768 layout: grid.clone(),
1769 orientation: Orientation::Vertical,
1770 },
1771 span.clone(),
1772 )
1773 .into(),
1774 );
1775 layout_info_prop_h.element().borrow_mut().bindings.insert(
1776 layout_info_prop_h.name().clone(),
1777 BindingExpression::new_with_span(
1778 Expression::ComputeGridLayoutInfo {
1779 layout_organized_data_prop: layout_organized_data_prop.clone(),
1780 layout: grid.clone(),
1781 orientation: Orientation::Horizontal,
1782 cross_axis_size: None,
1783 },
1784 span.clone(),
1785 )
1786 .into(),
1787 );
1788 layout_info_prop_v.element().borrow_mut().bindings.insert(
1789 layout_info_prop_v.name().clone(),
1790 BindingExpression::new_with_span(
1791 Expression::ComputeGridLayoutInfo {
1792 layout_organized_data_prop: layout_organized_data_prop.clone(),
1793 layout: grid.clone(),
1794 orientation: Orientation::Vertical,
1795 cross_axis_size: None,
1796 },
1797 span,
1798 )
1799 .into(),
1800 );
1801 dialog_element.borrow_mut().layout_info_prop = Some((layout_info_prop_h, layout_info_prop_v));
1802 for d in dialog_element.borrow_mut().debug.iter_mut() {
1803 d.layout = Some(Layout::GridLayout(grid.clone()));
1804 }
1805}
1806
1807struct CreateLayoutItemResult {
1808 item: LayoutItem,
1809 elem: ElementRc,
1810 repeater_index: Option<Expression>,
1811}
1812
1813fn create_layout_item(
1815 item_element: &ElementRc,
1816 diag: &mut BuildDiagnostics,
1817) -> CreateLayoutItemResult {
1818 let fix_explicit_percent = |prop: &str, item: &ElementRc| {
1819 if !item.borrow().bindings.get(prop).is_some_and(|b| b.borrow().ty() == Type::Percent) {
1820 return;
1821 }
1822 let min_name = format_smolstr!("min-{}", prop);
1823 let max_name = format_smolstr!("max-{}", prop);
1824 let mut min_ref = BindingExpression::from(Expression::PropertyReference(
1825 NamedReference::new(item, min_name.clone()),
1826 ));
1827 let mut item = item.borrow_mut();
1828 let b = item.bindings.remove(prop).unwrap().into_inner();
1829 min_ref.span = b.span.clone();
1830 min_ref.priority = b.priority;
1831 item.bindings.insert(max_name.clone(), min_ref.into());
1832 item.bindings.insert(min_name.clone(), b.into());
1833 item.property_declarations.insert(
1834 min_name,
1835 PropertyDeclaration { property_type: Type::Percent, ..PropertyDeclaration::default() },
1836 );
1837 item.property_declarations.insert(
1838 max_name,
1839 PropertyDeclaration { property_type: Type::Percent, ..PropertyDeclaration::default() },
1840 );
1841 };
1842 fix_explicit_percent("width", item_element);
1843 fix_explicit_percent("height", item_element);
1844
1845 item_element.borrow_mut().child_of_layout = true;
1846 let (repeater_index, actual_elem) = if let Some(r) = &item_element.borrow().repeated {
1847 let rep_comp = item_element.borrow().base_type.as_component().clone();
1848 fix_explicit_percent("width", &rep_comp.root_element);
1849 fix_explicit_percent("height", &rep_comp.root_element);
1850
1851 *rep_comp.root_constraints.borrow_mut() = LayoutConstraints::new(
1852 &rep_comp.root_element,
1853 Some((&mut *diag, DiagnosticLevel::Error)),
1854 );
1855 rep_comp.root_element.borrow_mut().child_of_layout = true;
1856 (
1857 Some(if r.is_conditional_element {
1858 Expression::NumberLiteral(0., Unit::None)
1859 } else {
1860 Expression::RepeaterIndexReference { element: Rc::downgrade(item_element) }
1861 }),
1862 rep_comp.root_element.clone(),
1863 )
1864 } else {
1865 (None, item_element.clone())
1866 };
1867
1868 let constraints = LayoutConstraints::new(&actual_elem, Some((diag, DiagnosticLevel::Error)));
1869 CreateLayoutItemResult {
1870 item: LayoutItem { element: item_element.clone(), constraints },
1871 elem: actual_elem,
1872 repeater_index,
1873 }
1874}
1875
1876fn set_grid_prop_from_cache(
1877 elem: &ElementRc,
1878 prop: &str,
1879 layout_cache_prop: &NamedReference,
1880 index: usize,
1881 repeater_index: &Option<Expression>,
1882 child_offset: usize,
1883 stride_expr: Option<&Expression>,
1885 inner_repeater_index: Option<Expression>,
1886 entries_per_item: usize,
1887 diag: &mut BuildDiagnostics,
1888) {
1889 if let Some(stride) = stride_expr {
1890 let repeater_index_boxed = repeater_index.as_ref().map(|x| Box::new(x.clone()));
1892 let expr = Expression::GridRepeaterCacheAccess {
1893 layout_cache_prop: layout_cache_prop.clone(),
1894 index,
1895 repeater_index: repeater_index_boxed.unwrap(),
1896 stride: Box::new(stride.clone()),
1897 child_offset,
1898 inner_repeater_index: inner_repeater_index.map(Box::new),
1899 entries_per_item,
1900 };
1901 insert_cache_prop_binding(expr, elem, prop, layout_cache_prop, diag);
1902 } else {
1903 set_prop_from_cache(
1905 elem,
1906 prop,
1907 layout_cache_prop,
1908 index,
1909 repeater_index,
1910 entries_per_item,
1911 diag,
1912 );
1913 }
1914}
1915
1916fn set_prop_from_cache(
1917 elem: &ElementRc,
1918 prop: &str,
1919 layout_cache_prop: &NamedReference,
1920 index: usize,
1921 repeater_index: &Option<Expression>,
1922 entries_per_item: usize,
1923 diag: &mut BuildDiagnostics,
1924) {
1925 let expr = Expression::LayoutCacheAccess {
1926 layout_cache_prop: layout_cache_prop.clone(),
1927 index,
1928 repeater_index: repeater_index.as_ref().map(|x| Box::new(x.clone())),
1929 entries_per_item,
1930 };
1931 insert_cache_prop_binding(expr, elem, prop, layout_cache_prop, diag);
1932}
1933
1934fn insert_cache_prop_binding(
1935 expr: Expression,
1936 elem: &ElementRc,
1937 prop: &str,
1938 layout_cache_prop: &NamedReference,
1939 diag: &mut BuildDiagnostics,
1940) {
1941 let old = elem.borrow_mut().bindings.insert(
1942 prop.into(),
1943 BindingExpression::new_with_span(
1944 expr,
1945 layout_cache_prop.element().borrow().to_source_location(),
1946 )
1947 .into(),
1948 );
1949 if let Some(old) = old.map(RefCell::into_inner) {
1950 diag.push_error(
1951 format!("The property '{prop}' cannot be set for elements placed in this layout, because the layout is already setting it"),
1952 &old,
1953 );
1954 }
1955}
1956
1957#[derive(Copy, Clone)]
1959struct RepeaterCacheParams<'a> {
1960 index: usize,
1962 rep_idx: &'a Option<Expression>,
1964 child_offset: usize,
1966 inner_rep_idx: &'a Option<Expression>,
1968}
1969
1970fn set_coord_prop_from_cache(
1972 elem: &ElementRc,
1973 constraints: &LayoutConstraints,
1974 layout_cache_prop_h: &NamedReference,
1975 layout_cache_prop_v: &NamedReference,
1976 repeater_params: &RepeaterCacheParams<'_>,
1977 stride_h: Option<&Expression>,
1978 stride_v: Option<&Expression>,
1979 diag: &mut BuildDiagnostics,
1980) {
1981 let has_repeater_indirection = stride_h.is_some();
1982 let cache_idx = repeater_params.index * 2;
1983 let pos_offset = repeater_params.child_offset;
1984 let size_offset = repeater_params.child_offset + 1;
1985 let inner_idx_clone = repeater_params.inner_rep_idx.clone();
1986
1987 let size_cache_idx = if has_repeater_indirection { cache_idx } else { cache_idx + 1 };
1989
1990 set_grid_prop_from_cache(
1991 elem,
1992 "x",
1993 layout_cache_prop_h,
1994 cache_idx,
1995 repeater_params.rep_idx,
1996 pos_offset,
1997 stride_h,
1998 inner_idx_clone.clone(),
1999 2,
2000 diag,
2001 );
2002 if !constraints.fixed_width {
2003 set_grid_prop_from_cache(
2004 elem,
2005 "width",
2006 layout_cache_prop_h,
2007 size_cache_idx,
2008 repeater_params.rep_idx,
2009 size_offset,
2010 stride_h,
2011 inner_idx_clone.clone(),
2012 2,
2013 diag,
2014 );
2015 }
2016 set_grid_prop_from_cache(
2017 elem,
2018 "y",
2019 layout_cache_prop_v,
2020 cache_idx,
2021 repeater_params.rep_idx,
2022 pos_offset,
2023 stride_v,
2024 inner_idx_clone.clone(),
2025 2,
2026 diag,
2027 );
2028 if !constraints.fixed_height {
2029 set_grid_prop_from_cache(
2030 elem,
2031 "height",
2032 layout_cache_prop_v,
2033 size_cache_idx,
2034 repeater_params.rep_idx,
2035 size_offset,
2036 stride_v,
2037 inner_idx_clone,
2038 2,
2039 diag,
2040 );
2041 }
2042}
2043
2044fn set_grid_rowcol_from_cache(
2047 elem: &ElementRc,
2048 organized_data_prop: &NamedReference,
2049 repeater_params: &RepeaterCacheParams<'_>,
2050 stride: Option<&Expression>,
2051 (row_expr, col_expr): (&Option<RowColExpr>, &Option<RowColExpr>),
2052 diag: &mut BuildDiagnostics,
2053) {
2054 let has_repeater_indirection = stride.is_some();
2055 let org_cache_idx = repeater_params.index * 4;
2056
2057 let col_cache_idx = org_cache_idx;
2060 let col_offset = if has_repeater_indirection { repeater_params.child_offset * 4 } else { 0 };
2061
2062 let (row_cache_idx, row_offset) = if has_repeater_indirection {
2063 (org_cache_idx, repeater_params.child_offset * 4 + 2)
2064 } else {
2065 (org_cache_idx + 2, 0)
2066 };
2067
2068 if col_expr.is_none() {
2069 set_grid_prop_from_cache(
2070 elem,
2071 "col",
2072 organized_data_prop,
2073 col_cache_idx,
2074 repeater_params.rep_idx,
2075 col_offset,
2076 stride,
2077 repeater_params.inner_rep_idx.clone(),
2078 4,
2079 diag,
2080 );
2081 }
2082 if row_expr.is_none() {
2083 set_grid_prop_from_cache(
2084 elem,
2085 "row",
2086 organized_data_prop,
2087 row_cache_idx,
2088 repeater_params.rep_idx,
2089 row_offset,
2090 stride,
2091 repeater_params.inner_rep_idx.clone(),
2092 4,
2093 diag,
2094 );
2095 }
2096}
2097
2098fn check_number_literal_is_positive_integer(
2102 expression: &Expression,
2103 name: &str,
2104 span: &dyn crate::diagnostics::Spanned,
2105 diag: &mut BuildDiagnostics,
2106) -> bool {
2107 match super::ignore_debug_hooks(expression) {
2108 Expression::NumberLiteral(v, Unit::None) => {
2109 if *v > u16::MAX as f64 || !v.trunc().approx_eq(v) {
2110 diag.push_error(format!("'{name}' must be a positive integer"), span);
2111 }
2112 true
2113 }
2114 Expression::UnaryOp { op: '-', sub } => {
2115 if let Expression::NumberLiteral(_, Unit::None) = super::ignore_debug_hooks(sub) {
2116 diag.push_error(format!("'{name}' must be a positive integer"), span);
2117 }
2118 true
2119 }
2120 Expression::Cast { from, .. } => {
2121 check_number_literal_is_positive_integer(from, name, span, diag)
2122 }
2123 _ => false,
2124 }
2125}
2126
2127fn recognized_layout_types() -> &'static [&'static str] {
2128 &["Row", "GridLayout", "HorizontalLayout", "VerticalLayout", "FlexboxLayout", "Dialog"]
2129}
2130
2131fn check_no_layout_properties(
2133 item: &ElementRc,
2134 layout_type: &Option<SmolStr>,
2135 parent_layout_type: &Option<SmolStr>,
2136 diag: &mut BuildDiagnostics,
2137) {
2138 let elem = item.borrow();
2139 for (prop, expr) in elem.bindings.iter() {
2140 if !matches!(parent_layout_type.as_deref(), Some("GridLayout") | Some("Row"))
2141 && matches!(prop.as_ref(), "col" | "row" | "colspan" | "rowspan")
2142 {
2143 diag.push_error(format!("{prop} used outside of a GridLayout's cell"), &*expr.borrow());
2144 }
2145 if parent_layout_type.as_deref() != Some("FlexboxLayout")
2146 && matches!(
2147 prop.as_ref(),
2148 "flex-grow" | "flex-shrink" | "flex-basis" | "flex-align-self" | "flex-order"
2149 )
2150 {
2151 diag.push_error(format!("{prop} used outside of a FlexboxLayout"), &*expr.borrow());
2152 }
2153 if parent_layout_type.as_deref() != Some("Dialog")
2154 && matches!(prop.as_ref(), "dialog-button-role")
2155 {
2156 diag.push_error(
2157 format!("{prop} used outside of a Dialog's direct child"),
2158 &*expr.borrow(),
2159 );
2160 }
2161 if (layout_type.is_none()
2162 || !recognized_layout_types().contains(&layout_type.as_ref().unwrap().as_str()))
2163 && matches!(
2164 prop.as_ref(),
2165 "padding" | "padding-left" | "padding-right" | "padding-top" | "padding-bottom"
2166 )
2167 && !check_inherits_layout(item)
2168 {
2169 diag.push_warning(
2170 format!("{prop} only has effect on layout elements"),
2171 &*expr.borrow(),
2172 );
2173 }
2174 }
2175
2176 fn check_inherits_layout(item: &ElementRc) -> bool {
2178 if let ElementType::Component(c) = &item.borrow().base_type {
2179 c.root_element.borrow().debug.iter().any(|d| d.layout.is_some())
2180 || check_inherits_layout(&c.root_element)
2181 } else {
2182 false
2183 }
2184 }
2185}
2186
2187pub fn check_window_layout(component: &Rc<Component>) {
2193 if component.root_constraints.borrow().fixed_height {
2194 adjust_window_layout(component, "height");
2195 }
2196 if component.root_constraints.borrow().fixed_width {
2197 adjust_window_layout(component, "width");
2198 }
2199}
2200
2201pub fn check_popup_layout(component: &Rc<Component>) {
2202 component.popup_windows.borrow().iter().for_each(|p| {
2203 if p.component.root_constraints.borrow().fixed_height {
2204 adjust_window_layout(&p.component, "height");
2205 }
2206
2207 if p.component.root_constraints.borrow().fixed_width {
2208 adjust_window_layout(&p.component, "width");
2209 }
2210 });
2211}
2212
2213fn adjust_window_layout(component: &Rc<Component>, prop: &'static str) {
2214 let new_prop = crate::layout::create_new_prop(
2215 &component.root_element,
2216 format_smolstr!("fixed-{prop}"),
2217 Type::LogicalLength,
2218 );
2219 {
2220 let mut root = component.root_element.borrow_mut();
2221 if let Some(b) = root.bindings.remove(prop) {
2222 root.bindings.insert(new_prop.name().clone(), b);
2223 };
2224 let mut analysis = root.property_analysis.borrow_mut();
2225 if let Some(a) = analysis.remove(prop) {
2226 analysis.insert(new_prop.name().clone(), a);
2227 };
2228 drop(analysis);
2229 root.bindings.insert(
2230 prop.into(),
2231 RefCell::new(Expression::PropertyReference(new_prop.clone()).into()),
2232 );
2233 }
2234
2235 let old_prop = NamedReference::new(&component.root_element, SmolStr::new_static(prop));
2236 crate::object_tree::visit_all_named_references(component, &mut |nr| {
2237 if nr == &old_prop {
2238 *nr = new_prop.clone()
2239 }
2240 });
2241}