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 sub_item.elem.borrow_mut().grid_layout_cell = Some(child_grid_cell);
1053
1054 let effective_inner_rep_idx = if is_nested_repeater {
1060 let model_idx = sub_item.repeater_index.clone().unwrap();
1062 Some(if let Some(ref base) = cumulative_pos {
1063 Expression::BinaryExpression {
1064 lhs: Box::new(base.clone()),
1065 rhs: Box::new(model_idx),
1066 op: '+',
1067 }
1068 } else {
1069 model_idx
1070 })
1071 } else {
1072 cumulative_pos.clone()
1074 };
1075
1076 let repeater_params = RepeaterCacheParams {
1077 index: jump_pos,
1078 rep_idx: &layout_item.repeater_index,
1079 child_offset: 0,
1080 inner_rep_idx: &effective_inner_rep_idx,
1081 };
1082 set_coord_prop_from_cache(
1084 &sub_item.elem,
1085 &sub_item.item.constraints,
1086 layout_cache_prop_h,
1087 layout_cache_prop_v,
1088 &repeater_params,
1089 Some(&stride_h_expr),
1090 Some(&stride_v_expr),
1091 diag,
1092 );
1093 set_grid_rowcol_from_cache(
1095 &sub_item.elem,
1096 organized_data_prop,
1097 &repeater_params,
1098 Some(&stride_org_expr),
1099 (&None::<RowColExpr>, &None::<RowColExpr>),
1100 diag,
1101 );
1102
1103 if is_nested_repeater {
1105 let (model_expr, is_conditional) = {
1109 let b = child.borrow();
1110 let r = b.repeated.as_ref().unwrap();
1111 (r.model.clone(), r.is_conditional_element)
1112 };
1113 let len_expr = if is_conditional {
1114 Expression::Condition {
1115 condition: Box::new(model_expr),
1116 true_expr: Box::new(Expression::NumberLiteral(1., Unit::None)),
1117 false_expr: Box::new(Expression::NumberLiteral(0., Unit::None)),
1118 }
1119 } else {
1120 Expression::FunctionCall {
1121 function: Callable::Builtin(BuiltinFunction::ArrayLength),
1122 arguments: vec![model_expr],
1123 source_location: None,
1124 }
1125 };
1126 cumulative_pos = Some(if let Some(prev) = cumulative_pos.take() {
1127 Expression::BinaryExpression {
1128 lhs: Box::new(prev),
1129 rhs: Box::new(len_expr),
1130 op: '+',
1131 }
1132 } else {
1133 len_expr
1134 });
1135 } else {
1136 cumulative_pos = Some(if let Some(prev) = cumulative_pos.take() {
1138 Expression::BinaryExpression {
1139 lhs: Box::new(prev),
1140 rhs: Box::new(Expression::NumberLiteral(1., Unit::None)),
1141 op: '+',
1142 }
1143 } else {
1144 Expression::NumberLiteral(1., Unit::None)
1145 });
1146 }
1147
1148 if is_nested_repeater {
1149 children_layout_items.push(RowChildTemplate::Repeated {
1150 item: sub_item.item,
1151 repeated_element: child.clone(),
1152 });
1153 } else {
1154 children_layout_items.push(RowChildTemplate::Static(sub_item.item));
1155 }
1156 }
1157
1158 *num_cached_items += 1;
1160 let grid_layout_cell = Rc::new(RefCell::new(GridLayoutCell {
1162 new_row: true,
1163 col_expr: RowColExpr::Auto,
1164 row_expr: RowColExpr::Auto,
1165 colspan_expr: RowColExpr::Literal(1),
1166 rowspan_expr: RowColExpr::Literal(1),
1167 child_items: Some(children_layout_items),
1168 }));
1169 let grid_layout_element = GridLayoutElement {
1170 cell: grid_layout_cell.clone(),
1171 item: layout_item.item.clone(),
1172 };
1173 comp.root_element.borrow_mut().grid_layout_cell = Some(grid_layout_cell);
1174 self.elems.push(grid_layout_element);
1175 }
1176 }
1177
1178 fn add_element_with_coord_as_expr(
1179 &mut self,
1180 item_element: &ElementRc,
1181 new_row: bool,
1182 (row_expr, col_expr): (&Option<RowColExpr>, &Option<RowColExpr>),
1183 (rowspan_expr, colspan_expr): (&Option<RowColExpr>, &Option<RowColExpr>),
1184 layout_cache_prop_h: &NamedReference,
1185 layout_cache_prop_v: &NamedReference,
1186 organized_data_prop: &NamedReference,
1187 diag: &mut BuildDiagnostics,
1188 num_cached_items: &mut usize,
1189 ) {
1190 let layout_item = create_layout_item(item_element, diag);
1191
1192 let has_repeater_indirection = layout_item.repeater_index.is_some();
1193 let stride_coord =
1195 has_repeater_indirection.then(|| Expression::NumberLiteral(2.0, Unit::None));
1196 let stride_org =
1197 has_repeater_indirection.then(|| Expression::NumberLiteral(4.0, Unit::None));
1198 let repeater_params = RepeaterCacheParams {
1199 index: *num_cached_items,
1200 rep_idx: &layout_item.repeater_index,
1201 child_offset: 0,
1202 inner_rep_idx: &None,
1203 };
1204 set_coord_prop_from_cache(
1205 &layout_item.elem,
1206 &layout_item.item.constraints,
1207 layout_cache_prop_h,
1208 layout_cache_prop_v,
1209 &repeater_params,
1210 stride_coord.as_ref(),
1211 stride_coord.as_ref(),
1212 diag,
1213 );
1214 set_grid_rowcol_from_cache(
1215 &layout_item.elem,
1216 organized_data_prop,
1217 &repeater_params,
1218 stride_org.as_ref(),
1219 (row_expr, col_expr),
1220 diag,
1221 );
1222
1223 let expr_or_default = |expr: &Option<RowColExpr>, default: RowColExpr| -> RowColExpr {
1224 match expr {
1225 Some(RowColExpr::Literal(v)) => RowColExpr::Literal(*v),
1226 Some(RowColExpr::Named(nr)) => RowColExpr::Named(nr.clone()),
1227 Some(RowColExpr::Auto) => RowColExpr::Auto,
1228 None => default,
1229 }
1230 };
1231
1232 let grid_layout_cell = Rc::new(RefCell::new(GridLayoutCell {
1233 new_row,
1234 col_expr: expr_or_default(col_expr, RowColExpr::Auto),
1235 row_expr: expr_or_default(row_expr, RowColExpr::Auto),
1236 colspan_expr: expr_or_default(colspan_expr, RowColExpr::Literal(1)),
1237 rowspan_expr: expr_or_default(rowspan_expr, RowColExpr::Literal(1)),
1238 child_items: None,
1239 }));
1240 let grid_layout_element =
1241 GridLayoutElement { cell: grid_layout_cell.clone(), item: layout_item.item.clone() };
1242 layout_item.elem.borrow_mut().grid_layout_cell = Some(grid_layout_cell);
1243 self.elems.push(grid_layout_element);
1244 *num_cached_items += 1;
1245 }
1246}
1247
1248fn lower_box_layout(
1249 layout_element: &ElementRc,
1250 diag: &mut BuildDiagnostics,
1251 orientation: Orientation,
1252) {
1253 let mut layout = BoxLayout {
1254 orientation,
1255 elems: Default::default(),
1256 geometry: LayoutGeometry::new(layout_element),
1257 cross_alignment: binding_reference(layout_element, "cross-axis-alignment"),
1258 };
1259
1260 let layout_cache_prop =
1261 create_new_prop(layout_element, SmolStr::new_static("layout-cache"), Type::LayoutCache);
1262 let layout_cache_ortho_prop = layout.cross_alignment.is_some().then(|| {
1263 create_new_prop(
1264 layout_element,
1265 SmolStr::new_static("layout-cache-ortho"),
1266 Type::LayoutCache,
1267 )
1268 });
1269 let layout_info_prop_v = create_new_prop(
1270 layout_element,
1271 SmolStr::new_static("layoutinfo-v"),
1272 layout_info_type().into(),
1273 );
1274 let layout_info_prop_h = create_new_prop(
1275 layout_element,
1276 SmolStr::new_static("layoutinfo-h"),
1277 layout_info_type().into(),
1278 );
1279
1280 let layout_children = std::mem::take(&mut layout_element.borrow_mut().children);
1281
1282 let (pos, size, pad, ortho) = match orientation {
1283 Orientation::Horizontal => ("x", "width", "y", "height"),
1284 Orientation::Vertical => ("y", "height", "x", "width"),
1285 };
1286 let stretch_bindings = layout_cache_ortho_prop.is_none().then(|| {
1288 let (begin_padding, end_padding) = match orientation {
1289 Orientation::Horizontal => {
1290 (&layout.geometry.padding.top, &layout.geometry.padding.bottom)
1291 }
1292 Orientation::Vertical => {
1293 (&layout.geometry.padding.left, &layout.geometry.padding.right)
1294 }
1295 };
1296 let pad_expr = begin_padding.clone().map(Expression::PropertyReference);
1297 let mut size_expr = Expression::PropertyReference(NamedReference::new(
1298 layout_element,
1299 SmolStr::new_static(ortho),
1300 ));
1301 for p in [begin_padding, end_padding].into_iter().flatten() {
1302 size_expr = Expression::BinaryExpression {
1303 lhs: Box::new(std::mem::take(&mut size_expr)),
1304 rhs: Box::new(Expression::PropertyReference(p.clone())),
1305 op: '-',
1306 };
1307 }
1308 (pad_expr, size_expr)
1309 });
1310
1311 for layout_child in &layout_children {
1312 let item = create_layout_item(layout_child, diag);
1313 let index = layout.elems.len() * 2;
1314 let rep_idx = &item.repeater_index;
1315 let (fixed_size, fixed_ortho) = match orientation {
1316 Orientation::Horizontal => {
1317 (item.item.constraints.fixed_width, item.item.constraints.fixed_height)
1318 }
1319 Orientation::Vertical => {
1320 (item.item.constraints.fixed_height, item.item.constraints.fixed_width)
1321 }
1322 };
1323 let actual_elem = &item.elem;
1324 set_prop_from_cache(actual_elem, pos, &layout_cache_prop, index, rep_idx, 2, diag);
1325 if !fixed_size {
1326 set_prop_from_cache(actual_elem, size, &layout_cache_prop, index + 1, rep_idx, 2, diag);
1327 }
1328 if let Some(cache_ortho) = &layout_cache_ortho_prop {
1329 set_prop_from_cache(actual_elem, pad, cache_ortho, index, rep_idx, 2, diag);
1330 if !fixed_ortho {
1331 set_prop_from_cache(actual_elem, ortho, cache_ortho, index + 1, rep_idx, 2, diag);
1332 }
1333 } else {
1334 let (pad_expr, size_expr) = stretch_bindings.as_ref().unwrap();
1335 if let Some(pad_expr) = pad_expr {
1336 actual_elem
1337 .borrow_mut()
1338 .bindings
1339 .insert(pad.into(), RefCell::new(pad_expr.clone().into()));
1340 }
1341 if !fixed_ortho {
1342 actual_elem
1343 .borrow_mut()
1344 .bindings
1345 .insert(ortho.into(), RefCell::new(size_expr.clone().into()));
1346 }
1347 }
1348 layout.elems.push(item.item);
1349 }
1350 layout_element.borrow_mut().children = layout_children;
1351 let span = layout_element.borrow().to_source_location();
1352 layout_cache_prop.element().borrow_mut().bindings.insert(
1353 layout_cache_prop.name().clone(),
1354 BindingExpression::new_with_span(
1355 Expression::SolveBoxLayout(layout.clone(), orientation),
1356 span.clone(),
1357 )
1358 .into(),
1359 );
1360 if let Some(cache_ortho) = &layout_cache_ortho_prop {
1361 cache_ortho.element().borrow_mut().bindings.insert(
1362 cache_ortho.name().clone(),
1363 BindingExpression::new_with_span(
1364 Expression::SolveBoxLayout(layout.clone(), orientation.orthogonal()),
1365 span.clone(),
1366 )
1367 .into(),
1368 );
1369 }
1370 layout_info_prop_h.element().borrow_mut().bindings.insert(
1371 layout_info_prop_h.name().clone(),
1372 BindingExpression::new_with_span(
1373 Expression::ComputeBoxLayoutInfo {
1374 layout: layout.clone(),
1375 orientation: Orientation::Horizontal,
1376 cross_axis_size: None,
1377 },
1378 span.clone(),
1379 )
1380 .into(),
1381 );
1382 layout_info_prop_v.element().borrow_mut().bindings.insert(
1383 layout_info_prop_v.name().clone(),
1384 BindingExpression::new_with_span(
1385 Expression::ComputeBoxLayoutInfo {
1386 layout: layout.clone(),
1387 orientation: Orientation::Vertical,
1388 cross_axis_size: None,
1389 },
1390 span,
1391 )
1392 .into(),
1393 );
1394 layout_element.borrow_mut().layout_info_prop = Some((layout_info_prop_h, layout_info_prop_v));
1395 for d in layout_element.borrow_mut().debug.iter_mut() {
1396 d.layout = Some(Layout::BoxLayout(layout.clone()));
1397 }
1398}
1399
1400fn lower_flexbox_layout(layout_element: &ElementRc, diag: &mut BuildDiagnostics) {
1401 if let Some(binding) = layout_element.borrow().bindings.get("alignment") {
1404 let binding = binding.borrow();
1405 if matches!(binding.expression.ignore_debug_hooks(),
1406 Expression::EnumerationValue(v) if v.enumeration.name == "LayoutAlignment"
1407 && v.enumeration.values[v.value] == "stretch")
1408 {
1409 diag.push_warning(
1410 "alignment: stretch has no effect on FlexboxLayout".into(),
1411 &*binding,
1412 );
1413 }
1414 }
1415
1416 let direction = crate::layout::binding_reference(layout_element, "flex-direction");
1417 let align_content = crate::layout::binding_reference(layout_element, "align-content");
1418 let cross_axis_alignment =
1419 crate::layout::binding_reference(layout_element, "cross-axis-alignment");
1420 let flex_wrap = crate::layout::binding_reference(layout_element, "flex-wrap");
1421
1422 let mut layout = crate::layout::FlexboxLayout {
1423 elems: Default::default(),
1424 geometry: LayoutGeometry::new(layout_element),
1425 direction,
1426 align_content,
1427 cross_axis_alignment,
1428 flex_wrap,
1429 };
1430
1431 let layout_cache_prop =
1433 create_new_prop(layout_element, SmolStr::new_static("layout-cache"), Type::LayoutCache);
1434 let layout_info_prop_v = create_new_prop(
1435 layout_element,
1436 SmolStr::new_static("layoutinfo-v"),
1437 layout_info_type().into(),
1438 );
1439 let layout_info_prop_h = create_new_prop(
1440 layout_element,
1441 SmolStr::new_static("layoutinfo-h"),
1442 layout_info_type().into(),
1443 );
1444
1445 let layout_children = std::mem::take(&mut layout_element.borrow_mut().children);
1446
1447 for layout_child in &layout_children {
1448 let item = create_layout_item(layout_child, diag);
1449 let index = layout.elems.len() * 4; let rep_idx = &item.repeater_index;
1451 let actual_elem = &item.elem;
1452
1453 set_prop_from_cache(actual_elem, "x", &layout_cache_prop, index, rep_idx, 4, diag);
1455 set_prop_from_cache(actual_elem, "y", &layout_cache_prop, index + 1, rep_idx, 4, diag);
1457 if !item.item.constraints.fixed_width {
1459 set_prop_from_cache(
1460 actual_elem,
1461 "width",
1462 &layout_cache_prop,
1463 index + 2,
1464 rep_idx,
1465 4,
1466 diag,
1467 );
1468 }
1469 if !item.item.constraints.fixed_height {
1471 set_prop_from_cache(
1472 actual_elem,
1473 "height",
1474 &layout_cache_prop,
1475 index + 3,
1476 rep_idx,
1477 4,
1478 diag,
1479 );
1480 }
1481 let flex_grow = crate::layout::binding_reference(actual_elem, "flex-grow");
1482 let flex_shrink = crate::layout::binding_reference(actual_elem, "flex-shrink");
1483 let flex_basis = crate::layout::binding_reference(actual_elem, "flex-basis");
1484 let align_self = crate::layout::binding_reference(actual_elem, "flex-align-self");
1485 let order = crate::layout::binding_reference(actual_elem, "flex-order");
1486 layout.elems.push(crate::layout::FlexboxLayoutItem {
1487 item: item.item,
1488 flex_grow,
1489 flex_shrink,
1490 flex_basis,
1491 align_self,
1492 order,
1493 });
1494 }
1495 layout_element.borrow_mut().children = layout_children;
1496 let span = layout_element.borrow().to_source_location();
1497
1498 layout_cache_prop.element().borrow_mut().bindings.insert(
1499 layout_cache_prop.name().clone(),
1500 BindingExpression::new_with_span(
1501 Expression::SolveFlexboxLayout(layout.clone()),
1502 span.clone(),
1503 )
1504 .into(),
1505 );
1506 layout_info_prop_h.element().borrow_mut().bindings.insert(
1507 layout_info_prop_h.name().clone(),
1508 BindingExpression::new_with_span(
1509 Expression::ComputeFlexboxLayoutInfo {
1510 layout: layout.clone(),
1511 orientation: Orientation::Horizontal,
1512 cross_axis_size: None,
1513 },
1514 span.clone(),
1515 )
1516 .into(),
1517 );
1518 layout_info_prop_v.element().borrow_mut().bindings.insert(
1519 layout_info_prop_v.name().clone(),
1520 BindingExpression::new_with_span(
1521 Expression::ComputeFlexboxLayoutInfo {
1522 layout: layout.clone(),
1523 orientation: Orientation::Vertical,
1524 cross_axis_size: None,
1525 },
1526 span,
1527 )
1528 .into(),
1529 );
1530 layout_element.borrow_mut().layout_info_prop = Some((layout_info_prop_h, layout_info_prop_v));
1531 for d in layout_element.borrow_mut().debug.iter_mut() {
1532 d.layout = Some(Layout::FlexboxLayout(layout.clone()));
1533 }
1534}
1535
1536fn lower_dialog_layout(
1537 dialog_element: &ElementRc,
1538 style_metrics: &Rc<Component>,
1539 diag: &mut BuildDiagnostics,
1540) {
1541 let mut grid = GridLayout {
1542 elems: Default::default(),
1543 geometry: LayoutGeometry::new(dialog_element),
1544 dialog_button_roles: None,
1545 uses_auto: true,
1546 };
1547 let metrics = &style_metrics.root_element;
1548 grid.geometry
1549 .padding
1550 .bottom
1551 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-padding")));
1552 grid.geometry
1553 .padding
1554 .top
1555 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-padding")));
1556 grid.geometry
1557 .padding
1558 .left
1559 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-padding")));
1560 grid.geometry
1561 .padding
1562 .right
1563 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-padding")));
1564 grid.geometry
1565 .spacing
1566 .horizontal
1567 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-spacing")));
1568 grid.geometry
1569 .spacing
1570 .vertical
1571 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-spacing")));
1572
1573 let layout_organized_data_prop = create_new_prop(
1574 dialog_element,
1575 SmolStr::new_static("layout-organized-data"),
1576 Type::ArrayOfU16,
1577 );
1578 let layout_cache_prop_h =
1579 create_new_prop(dialog_element, SmolStr::new_static("layout-cache-h"), Type::LayoutCache);
1580 let layout_cache_prop_v =
1581 create_new_prop(dialog_element, SmolStr::new_static("layout-cache-v"), Type::LayoutCache);
1582 let layout_info_prop_h = create_new_prop(
1583 dialog_element,
1584 SmolStr::new_static("layoutinfo-h"),
1585 layout_info_type().into(),
1586 );
1587 let layout_info_prop_v = create_new_prop(
1588 dialog_element,
1589 SmolStr::new_static("layoutinfo-v"),
1590 layout_info_type().into(),
1591 );
1592
1593 let mut main_widget = None;
1594 let mut button_roles = Vec::new();
1595 let mut seen_buttons = HashSet::new();
1596 let mut num_cached_items: usize = 0;
1597 let layout_children = std::mem::take(&mut dialog_element.borrow_mut().children);
1598 for layout_child in &layout_children {
1599 let dialog_button_role_binding =
1600 layout_child.borrow_mut().bindings.remove("dialog-button-role");
1601 let is_button = if let Some(role_binding) = dialog_button_role_binding {
1602 let role_binding = role_binding.into_inner();
1603 if let Expression::EnumerationValue(val) =
1604 super::ignore_debug_hooks(&role_binding.expression)
1605 {
1606 let en = &val.enumeration;
1607 debug_assert_eq!(en.name, "DialogButtonRole");
1608 button_roles.push(en.values[val.value].clone());
1609 if val.value == 0 {
1610 diag.push_error(
1611 "The `dialog-button-role` cannot be set explicitly to none".into(),
1612 &role_binding,
1613 );
1614 }
1615 } else {
1616 diag.push_error(
1617 "The `dialog-button-role` property must be known at compile-time".into(),
1618 &role_binding,
1619 );
1620 }
1621 true
1622 } else if matches!(&layout_child.borrow().lookup_property("kind").property_type, Type::Enumeration(e) if e.name == "StandardButtonKind")
1623 {
1624 match layout_child.borrow().bindings.get("kind") {
1626 None => diag.push_error(
1627 "The `kind` property of the StandardButton in a Dialog must be set".into(),
1628 &*layout_child.borrow(),
1629 ),
1630 Some(binding) => {
1631 let binding = &*binding.borrow();
1632 if let Expression::EnumerationValue(val) =
1633 super::ignore_debug_hooks(&binding.expression)
1634 {
1635 let en = &val.enumeration;
1636 debug_assert_eq!(en.name, "StandardButtonKind");
1637 let kind = &en.values[val.value];
1638 let role = match kind.as_str() {
1639 "ok" => "accept",
1640 "cancel" => "reject",
1641 "apply" => "apply",
1642 "close" => "reject",
1643 "reset" => "reset",
1644 "help" => "help",
1645 "yes" => "accept",
1646 "no" => "reject",
1647 "abort" => "reject",
1648 "retry" => "accept",
1649 "ignore" => "accept",
1650 _ => unreachable!(),
1651 };
1652 button_roles.push(role.into());
1653 if !seen_buttons.insert(val.value) {
1654 diag.push_error("Duplicated `kind`: There are two StandardButton in this Dialog with the same kind".into(), binding);
1655 } else if Rc::ptr_eq(
1656 dialog_element,
1657 &dialog_element
1658 .borrow()
1659 .enclosing_component
1660 .upgrade()
1661 .unwrap()
1662 .root_element,
1663 ) {
1664 let clicked_ty =
1665 layout_child.borrow().lookup_property("clicked").property_type;
1666 if matches!(&clicked_ty, Type::Callback { .. })
1667 && layout_child.borrow().bindings.get("clicked").is_none_or(|c| {
1668 matches!(c.borrow().expression, Expression::Invalid)
1669 })
1670 {
1671 dialog_element
1672 .borrow_mut()
1673 .property_declarations
1674 .entry(format_smolstr!("{}-clicked", kind))
1675 .or_insert_with(|| PropertyDeclaration {
1676 property_type: clicked_ty,
1677 node: None,
1678 expose_in_public_api: true,
1679 is_alias: Some(NamedReference::new(
1680 layout_child,
1681 SmolStr::new_static("clicked"),
1682 )),
1683 visibility: PropertyVisibility::InOut,
1684 pure: None,
1685 shadows_builtin: false,
1686 });
1687 }
1688 }
1689 } else {
1690 diag.push_error(
1691 "The `kind` property of the StandardButton in a Dialog must be known at compile-time"
1692 .into(),
1693 binding,
1694 );
1695 }
1696 }
1697 }
1698 true
1699 } else {
1700 false
1701 };
1702
1703 if is_button {
1704 grid.add_element_with_coord(
1705 layout_child,
1706 (1, button_roles.len() as u16),
1707 (1, 1),
1708 &layout_cache_prop_h,
1709 &layout_cache_prop_v,
1710 &layout_organized_data_prop,
1711 diag,
1712 &mut num_cached_items,
1713 );
1714 } else if main_widget.is_some() {
1715 diag.push_error(
1716 "A Dialog can have only one child element that is not a StandardButton".into(),
1717 &*layout_child.borrow(),
1718 );
1719 } else {
1720 main_widget = Some(layout_child.clone())
1721 }
1722 }
1723 dialog_element.borrow_mut().children = layout_children;
1724
1725 if let Some(main_widget) = main_widget {
1726 grid.add_element_with_coord(
1727 &main_widget,
1728 (0, 0),
1729 (1, button_roles.len() as u16 + 1),
1730 &layout_cache_prop_h,
1731 &layout_cache_prop_v,
1732 &layout_organized_data_prop,
1733 diag,
1734 &mut num_cached_items,
1735 );
1736 } else {
1737 diag.push_error(
1738 "A Dialog must have a single child element that is not StandardButton".into(),
1739 &*dialog_element.borrow(),
1740 );
1741 }
1742 grid.dialog_button_roles = Some(button_roles);
1743
1744 let span = dialog_element.borrow().to_source_location();
1745 layout_organized_data_prop.element().borrow_mut().bindings.insert(
1746 layout_organized_data_prop.name().clone(),
1747 BindingExpression::new_with_span(
1748 Expression::OrganizeGridLayout(grid.clone()),
1749 span.clone(),
1750 )
1751 .into(),
1752 );
1753 layout_cache_prop_h.element().borrow_mut().bindings.insert(
1754 layout_cache_prop_h.name().clone(),
1755 BindingExpression::new_with_span(
1756 Expression::SolveGridLayout {
1757 layout_organized_data_prop: layout_organized_data_prop.clone(),
1758 layout: grid.clone(),
1759 orientation: Orientation::Horizontal,
1760 },
1761 span.clone(),
1762 )
1763 .into(),
1764 );
1765 layout_cache_prop_v.element().borrow_mut().bindings.insert(
1766 layout_cache_prop_v.name().clone(),
1767 BindingExpression::new_with_span(
1768 Expression::SolveGridLayout {
1769 layout_organized_data_prop: layout_organized_data_prop.clone(),
1770 layout: grid.clone(),
1771 orientation: Orientation::Vertical,
1772 },
1773 span.clone(),
1774 )
1775 .into(),
1776 );
1777 layout_info_prop_h.element().borrow_mut().bindings.insert(
1778 layout_info_prop_h.name().clone(),
1779 BindingExpression::new_with_span(
1780 Expression::ComputeGridLayoutInfo {
1781 layout_organized_data_prop: layout_organized_data_prop.clone(),
1782 layout: grid.clone(),
1783 orientation: Orientation::Horizontal,
1784 cross_axis_size: None,
1785 },
1786 span.clone(),
1787 )
1788 .into(),
1789 );
1790 layout_info_prop_v.element().borrow_mut().bindings.insert(
1791 layout_info_prop_v.name().clone(),
1792 BindingExpression::new_with_span(
1793 Expression::ComputeGridLayoutInfo {
1794 layout_organized_data_prop: layout_organized_data_prop.clone(),
1795 layout: grid.clone(),
1796 orientation: Orientation::Vertical,
1797 cross_axis_size: None,
1798 },
1799 span,
1800 )
1801 .into(),
1802 );
1803 dialog_element.borrow_mut().layout_info_prop = Some((layout_info_prop_h, layout_info_prop_v));
1804 for d in dialog_element.borrow_mut().debug.iter_mut() {
1805 d.layout = Some(Layout::GridLayout(grid.clone()));
1806 }
1807}
1808
1809struct CreateLayoutItemResult {
1810 item: LayoutItem,
1811 elem: ElementRc,
1812 repeater_index: Option<Expression>,
1813}
1814
1815fn create_layout_item(
1817 item_element: &ElementRc,
1818 diag: &mut BuildDiagnostics,
1819) -> CreateLayoutItemResult {
1820 let fix_explicit_percent = |prop: &str, item: &ElementRc| {
1821 if !item.borrow().bindings.get(prop).is_some_and(|b| b.borrow().ty() == Type::Percent) {
1822 return;
1823 }
1824 let min_name = format_smolstr!("min-{}", prop);
1825 let max_name = format_smolstr!("max-{}", prop);
1826 let mut min_ref = BindingExpression::from(Expression::PropertyReference(
1827 NamedReference::new(item, min_name.clone()),
1828 ));
1829 let mut item = item.borrow_mut();
1830 let b = item.bindings.remove(prop).unwrap().into_inner();
1831 min_ref.span = b.span.clone();
1832 min_ref.priority = b.priority;
1833 item.bindings.insert(max_name.clone(), min_ref.into());
1834 item.bindings.insert(min_name.clone(), b.into());
1835 item.property_declarations.insert(
1836 min_name,
1837 PropertyDeclaration { property_type: Type::Percent, ..PropertyDeclaration::default() },
1838 );
1839 item.property_declarations.insert(
1840 max_name,
1841 PropertyDeclaration { property_type: Type::Percent, ..PropertyDeclaration::default() },
1842 );
1843 };
1844 fix_explicit_percent("width", item_element);
1845 fix_explicit_percent("height", item_element);
1846
1847 item_element.borrow_mut().child_of_layout = true;
1848 let (repeater_index, actual_elem) = if let Some(r) = &item_element.borrow().repeated {
1849 let rep_comp = item_element.borrow().base_type.as_component().clone();
1850 fix_explicit_percent("width", &rep_comp.root_element);
1851 fix_explicit_percent("height", &rep_comp.root_element);
1852
1853 *rep_comp.root_constraints.borrow_mut() = LayoutConstraints::new(
1854 &rep_comp.root_element,
1855 Some((&mut *diag, DiagnosticLevel::Error)),
1856 );
1857 rep_comp.root_element.borrow_mut().child_of_layout = true;
1858 (
1859 Some(if r.is_conditional_element {
1860 Expression::NumberLiteral(0., Unit::None)
1861 } else {
1862 Expression::RepeaterIndexReference { element: Rc::downgrade(item_element) }
1863 }),
1864 rep_comp.root_element.clone(),
1865 )
1866 } else {
1867 (None, item_element.clone())
1868 };
1869
1870 let constraints = LayoutConstraints::new(&actual_elem, Some((diag, DiagnosticLevel::Error)));
1871 CreateLayoutItemResult {
1872 item: LayoutItem { element: item_element.clone(), constraints },
1873 elem: actual_elem,
1874 repeater_index,
1875 }
1876}
1877
1878fn set_grid_prop_from_cache(
1879 elem: &ElementRc,
1880 prop: &str,
1881 layout_cache_prop: &NamedReference,
1882 index: usize,
1883 repeater_index: &Option<Expression>,
1884 child_offset: usize,
1885 stride_expr: Option<&Expression>,
1887 inner_repeater_index: Option<Expression>,
1888 entries_per_item: usize,
1889 diag: &mut BuildDiagnostics,
1890) {
1891 if let Some(stride) = stride_expr {
1892 let repeater_index_boxed = repeater_index.as_ref().map(|x| Box::new(x.clone()));
1894 let expr = Expression::GridRepeaterCacheAccess {
1895 layout_cache_prop: layout_cache_prop.clone(),
1896 index,
1897 repeater_index: repeater_index_boxed.unwrap(),
1898 stride: Box::new(stride.clone()),
1899 child_offset,
1900 inner_repeater_index: inner_repeater_index.map(Box::new),
1901 entries_per_item,
1902 };
1903 insert_cache_prop_binding(expr, elem, prop, layout_cache_prop, diag);
1904 } else {
1905 set_prop_from_cache(
1907 elem,
1908 prop,
1909 layout_cache_prop,
1910 index,
1911 repeater_index,
1912 entries_per_item,
1913 diag,
1914 );
1915 }
1916}
1917
1918fn set_prop_from_cache(
1919 elem: &ElementRc,
1920 prop: &str,
1921 layout_cache_prop: &NamedReference,
1922 index: usize,
1923 repeater_index: &Option<Expression>,
1924 entries_per_item: usize,
1925 diag: &mut BuildDiagnostics,
1926) {
1927 let expr = Expression::LayoutCacheAccess {
1928 layout_cache_prop: layout_cache_prop.clone(),
1929 index,
1930 repeater_index: repeater_index.as_ref().map(|x| Box::new(x.clone())),
1931 entries_per_item,
1932 };
1933 insert_cache_prop_binding(expr, elem, prop, layout_cache_prop, diag);
1934}
1935
1936fn insert_cache_prop_binding(
1937 expr: Expression,
1938 elem: &ElementRc,
1939 prop: &str,
1940 layout_cache_prop: &NamedReference,
1941 diag: &mut BuildDiagnostics,
1942) {
1943 let old = elem.borrow_mut().bindings.insert(
1944 prop.into(),
1945 BindingExpression::new_with_span(
1946 expr,
1947 layout_cache_prop.element().borrow().to_source_location(),
1948 )
1949 .into(),
1950 );
1951 if let Some(old) = old.map(RefCell::into_inner) {
1952 diag.push_error(
1953 format!("The property '{prop}' cannot be set for elements placed in this layout, because the layout is already setting it"),
1954 &old,
1955 );
1956 }
1957}
1958
1959#[derive(Copy, Clone)]
1961struct RepeaterCacheParams<'a> {
1962 index: usize,
1964 rep_idx: &'a Option<Expression>,
1966 child_offset: usize,
1968 inner_rep_idx: &'a Option<Expression>,
1970}
1971
1972fn set_coord_prop_from_cache(
1974 elem: &ElementRc,
1975 constraints: &LayoutConstraints,
1976 layout_cache_prop_h: &NamedReference,
1977 layout_cache_prop_v: &NamedReference,
1978 repeater_params: &RepeaterCacheParams<'_>,
1979 stride_h: Option<&Expression>,
1980 stride_v: Option<&Expression>,
1981 diag: &mut BuildDiagnostics,
1982) {
1983 let has_repeater_indirection = stride_h.is_some();
1984 let cache_idx = repeater_params.index * 2;
1985 let pos_offset = repeater_params.child_offset;
1986 let size_offset = repeater_params.child_offset + 1;
1987 let inner_idx_clone = repeater_params.inner_rep_idx.clone();
1988
1989 let size_cache_idx = if has_repeater_indirection { cache_idx } else { cache_idx + 1 };
1991
1992 set_grid_prop_from_cache(
1993 elem,
1994 "x",
1995 layout_cache_prop_h,
1996 cache_idx,
1997 repeater_params.rep_idx,
1998 pos_offset,
1999 stride_h,
2000 inner_idx_clone.clone(),
2001 2,
2002 diag,
2003 );
2004 if !constraints.fixed_width {
2005 set_grid_prop_from_cache(
2006 elem,
2007 "width",
2008 layout_cache_prop_h,
2009 size_cache_idx,
2010 repeater_params.rep_idx,
2011 size_offset,
2012 stride_h,
2013 inner_idx_clone.clone(),
2014 2,
2015 diag,
2016 );
2017 }
2018 set_grid_prop_from_cache(
2019 elem,
2020 "y",
2021 layout_cache_prop_v,
2022 cache_idx,
2023 repeater_params.rep_idx,
2024 pos_offset,
2025 stride_v,
2026 inner_idx_clone.clone(),
2027 2,
2028 diag,
2029 );
2030 if !constraints.fixed_height {
2031 set_grid_prop_from_cache(
2032 elem,
2033 "height",
2034 layout_cache_prop_v,
2035 size_cache_idx,
2036 repeater_params.rep_idx,
2037 size_offset,
2038 stride_v,
2039 inner_idx_clone,
2040 2,
2041 diag,
2042 );
2043 }
2044}
2045
2046fn set_grid_rowcol_from_cache(
2049 elem: &ElementRc,
2050 organized_data_prop: &NamedReference,
2051 repeater_params: &RepeaterCacheParams<'_>,
2052 stride: Option<&Expression>,
2053 (row_expr, col_expr): (&Option<RowColExpr>, &Option<RowColExpr>),
2054 diag: &mut BuildDiagnostics,
2055) {
2056 let has_repeater_indirection = stride.is_some();
2057 let org_cache_idx = repeater_params.index * 4;
2058
2059 let col_cache_idx = org_cache_idx;
2062 let col_offset = if has_repeater_indirection { repeater_params.child_offset * 4 } else { 0 };
2063
2064 let (row_cache_idx, row_offset) = if has_repeater_indirection {
2065 (org_cache_idx, repeater_params.child_offset * 4 + 2)
2066 } else {
2067 (org_cache_idx + 2, 0)
2068 };
2069
2070 if col_expr.is_none() {
2071 set_grid_prop_from_cache(
2072 elem,
2073 "col",
2074 organized_data_prop,
2075 col_cache_idx,
2076 repeater_params.rep_idx,
2077 col_offset,
2078 stride,
2079 repeater_params.inner_rep_idx.clone(),
2080 4,
2081 diag,
2082 );
2083 }
2084 if row_expr.is_none() {
2085 set_grid_prop_from_cache(
2086 elem,
2087 "row",
2088 organized_data_prop,
2089 row_cache_idx,
2090 repeater_params.rep_idx,
2091 row_offset,
2092 stride,
2093 repeater_params.inner_rep_idx.clone(),
2094 4,
2095 diag,
2096 );
2097 }
2098}
2099
2100fn check_number_literal_is_positive_integer(
2104 expression: &Expression,
2105 name: &str,
2106 span: &dyn crate::diagnostics::Spanned,
2107 diag: &mut BuildDiagnostics,
2108) -> bool {
2109 match super::ignore_debug_hooks(expression) {
2110 Expression::NumberLiteral(v, Unit::None) => {
2111 if *v > u16::MAX as f64 || !v.trunc().approx_eq(v) {
2112 diag.push_error(format!("'{name}' must be a positive integer"), span);
2113 }
2114 true
2115 }
2116 Expression::UnaryOp { op: '-', sub } => {
2117 if let Expression::NumberLiteral(_, Unit::None) = super::ignore_debug_hooks(sub) {
2118 diag.push_error(format!("'{name}' must be a positive integer"), span);
2119 }
2120 true
2121 }
2122 Expression::Cast { from, .. } => {
2123 check_number_literal_is_positive_integer(from, name, span, diag)
2124 }
2125 _ => false,
2126 }
2127}
2128
2129fn recognized_layout_types() -> &'static [&'static str] {
2130 &["Row", "GridLayout", "HorizontalLayout", "VerticalLayout", "FlexboxLayout", "Dialog"]
2131}
2132
2133fn check_no_layout_properties(
2135 item: &ElementRc,
2136 layout_type: &Option<SmolStr>,
2137 parent_layout_type: &Option<SmolStr>,
2138 diag: &mut BuildDiagnostics,
2139) {
2140 let elem = item.borrow();
2141 for (prop, expr) in elem.bindings.iter() {
2142 if !matches!(parent_layout_type.as_deref(), Some("GridLayout") | Some("Row"))
2143 && matches!(prop.as_ref(), "col" | "row" | "colspan" | "rowspan")
2144 {
2145 diag.push_error(format!("{prop} used outside of a GridLayout's cell"), &*expr.borrow());
2146 }
2147 if parent_layout_type.as_deref() != Some("FlexboxLayout")
2148 && matches!(
2149 prop.as_ref(),
2150 "flex-grow" | "flex-shrink" | "flex-basis" | "flex-align-self" | "flex-order"
2151 )
2152 {
2153 diag.push_error(format!("{prop} used outside of a FlexboxLayout"), &*expr.borrow());
2154 }
2155 if parent_layout_type.as_deref() != Some("Dialog")
2156 && matches!(prop.as_ref(), "dialog-button-role")
2157 {
2158 diag.push_error(
2159 format!("{prop} used outside of a Dialog's direct child"),
2160 &*expr.borrow(),
2161 );
2162 }
2163 if (layout_type.is_none()
2164 || !recognized_layout_types().contains(&layout_type.as_ref().unwrap().as_str()))
2165 && matches!(
2166 prop.as_ref(),
2167 "padding" | "padding-left" | "padding-right" | "padding-top" | "padding-bottom"
2168 )
2169 && !check_inherits_layout(item)
2170 {
2171 diag.push_warning(
2172 format!("{prop} only has effect on layout elements"),
2173 &*expr.borrow(),
2174 );
2175 }
2176 }
2177
2178 fn check_inherits_layout(item: &ElementRc) -> bool {
2180 if let ElementType::Component(c) = &item.borrow().base_type {
2181 c.root_element.borrow().debug.iter().any(|d| d.layout.is_some())
2182 || check_inherits_layout(&c.root_element)
2183 } else {
2184 false
2185 }
2186 }
2187}
2188
2189pub fn check_window_layout(component: &Rc<Component>) {
2195 if component.root_constraints.borrow().fixed_height {
2196 adjust_window_layout(component, "height");
2197 }
2198 if component.root_constraints.borrow().fixed_width {
2199 adjust_window_layout(component, "width");
2200 }
2201}
2202
2203pub fn check_popup_layout(component: &Rc<Component>) {
2204 component.popup_windows.borrow().iter().for_each(|p| {
2205 if p.component.root_constraints.borrow().fixed_height {
2206 adjust_window_layout(&p.component, "height");
2207 }
2208
2209 if p.component.root_constraints.borrow().fixed_width {
2210 adjust_window_layout(&p.component, "width");
2211 }
2212 });
2213}
2214
2215fn adjust_window_layout(component: &Rc<Component>, prop: &'static str) {
2216 let new_prop = crate::layout::create_new_prop(
2217 &component.root_element,
2218 format_smolstr!("fixed-{prop}"),
2219 Type::LogicalLength,
2220 );
2221 {
2222 let mut root = component.root_element.borrow_mut();
2223 if let Some(b) = root.bindings.remove(prop) {
2224 root.bindings.insert(new_prop.name().clone(), b);
2225 };
2226 let mut analysis = root.property_analysis.borrow_mut();
2227 if let Some(a) = analysis.remove(prop) {
2228 analysis.insert(new_prop.name().clone(), a);
2229 };
2230 drop(analysis);
2231 root.bindings.insert(
2232 prop.into(),
2233 RefCell::new(Expression::PropertyReference(new_prop.clone()).into()),
2234 );
2235 }
2236
2237 let old_prop = NamedReference::new(&component.root_element, SmolStr::new_static(prop));
2238 crate::object_tree::visit_all_named_references(component, &mut |nr| {
2239 if nr == &old_prop {
2240 *nr = new_prop.clone()
2241 }
2242 });
2243}