Skip to main content

i_slint_compiler/llr/
lower_layout_expression.rs

1// Copyright © SixtyFPS GmbH <info@slint.dev>
2// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0
3
4use std::collections::BTreeMap;
5use std::rc::Rc;
6
7use itertools::Either;
8use smol_str::SmolStr;
9
10use super::lower_to_item_tree::LoweredElement;
11use super::{GridLayoutRepeatedElement, LayoutRepeatedElement};
12use crate::langtype::{BuiltinPrivateStruct, EnumerationValue, Struct, Type};
13use crate::layout::{GridLayoutCell, Orientation, RowColExpr};
14use crate::llr::ArrayOutput as llr_ArrayOutput;
15use crate::llr::Expression as llr_Expression;
16use crate::namedreference::NamedReference;
17use crate::object_tree::ElementRc;
18
19use super::lower_expression::{ExpressionLoweringCtx, make_struct};
20
21fn empty_int32_slice() -> llr_Expression {
22    llr_Expression::Array {
23        element_ty: Type::Int32,
24        values: Vec::new(),
25        output: llr_ArrayOutput::Slice,
26    }
27}
28
29pub(super) fn compute_grid_layout_info(
30    layout_organized_data_prop: &NamedReference,
31    layout: &crate::layout::GridLayout,
32    o: Orientation,
33    ctx: &mut ExpressionLoweringCtx,
34) -> llr_Expression {
35    let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
36    let organized_cells = ctx.map_property_reference(layout_organized_data_prop);
37    let constraints_result = grid_layout_cell_constraints(layout, o, ctx);
38    let orientation_literal = llr_Expression::EnumerationValue(EnumerationValue {
39        value: o as _,
40        enumeration: crate::typeregister::BUILTIN.with(|b| b.enums.Orientation.clone()),
41    });
42
43    let sub_expression = llr_Expression::ExtraBuiltinFunctionCall {
44        function: "grid_layout_info".into(),
45        arguments: vec![
46            llr_Expression::PropertyReference(organized_cells),
47            constraints_result.cells,
48            if constraints_result.compute_cells.is_none() {
49                empty_int32_slice()
50            } else {
51                llr_Expression::ReadLocalVariable {
52                    name: "repeated_indices".into(),
53                    ty: Type::Array(Type::Int32.into()),
54                }
55            },
56            if constraints_result.compute_cells.is_none() {
57                empty_int32_slice()
58            } else {
59                llr_Expression::ReadLocalVariable {
60                    name: "repeater_steps".into(),
61                    ty: Type::Array(Type::Int32.into()),
62                }
63            },
64            spacing,
65            padding,
66            orientation_literal,
67        ],
68        return_ty: crate::typeregister::layout_info_type().into(),
69    };
70    match constraints_result.compute_cells {
71        Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
72            cells_variable,
73            repeater_indices_var_name: Some("repeated_indices".into()),
74            repeater_steps_var_name: Some("repeater_steps".into()),
75            elements,
76            orientation: o,
77            sub_expression: Box::new(sub_expression),
78        },
79        None => sub_expression,
80    }
81}
82
83pub(super) fn compute_box_layout_info(
84    layout: &crate::layout::BoxLayout,
85    o: Orientation,
86    ctx: &mut ExpressionLoweringCtx,
87) -> llr_Expression {
88    let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
89    let bld = box_layout_data(layout, o, ctx);
90    let sub_expression = if o == layout.orientation {
91        llr_Expression::ExtraBuiltinFunctionCall {
92            function: "box_layout_info".into(),
93            arguments: vec![bld.cells, spacing, padding, bld.alignment],
94            return_ty: crate::typeregister::layout_info_type().into(),
95        }
96    } else {
97        llr_Expression::ExtraBuiltinFunctionCall {
98            function: "box_layout_info_ortho".into(),
99            arguments: vec![bld.cells, padding],
100            return_ty: crate::typeregister::layout_info_type().into(),
101        }
102    };
103    match bld.compute_cells {
104        Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
105            cells_variable,
106            repeater_indices_var_name: None,
107            repeater_steps_var_name: None,
108            elements,
109            orientation: o,
110            sub_expression: Box::new(sub_expression),
111        },
112        None => sub_expression,
113    }
114}
115
116pub(super) fn organize_grid_layout(
117    layout: &crate::layout::GridLayout,
118    ctx: &mut ExpressionLoweringCtx,
119) -> llr_Expression {
120    let input_data = grid_layout_input_data(layout, ctx);
121
122    if let Some(button_roles) = &layout.dialog_button_roles {
123        let e = crate::typeregister::BUILTIN.with(|e| e.enums.DialogButtonRole.clone());
124        let roles = button_roles
125            .iter()
126            .map(|r| {
127                llr_Expression::EnumerationValue(EnumerationValue {
128                    value: e.values.iter().position(|x| x == r).unwrap() as _,
129                    enumeration: e.clone(),
130                })
131            })
132            .collect();
133        let roles_expr = llr_Expression::Array {
134            element_ty: Type::Enumeration(e),
135            values: roles,
136            output: llr_ArrayOutput::Slice,
137        };
138        llr_Expression::ExtraBuiltinFunctionCall {
139            function: "organize_dialog_button_layout".into(),
140            arguments: vec![input_data.cells, roles_expr],
141            return_ty: Type::Array(Type::Int32.into()),
142        }
143    } else {
144        let sub_expression = llr_Expression::ExtraBuiltinFunctionCall {
145            function: "organize_grid_layout".into(),
146            arguments: vec![
147                input_data.cells,
148                if input_data.compute_cells.is_none() {
149                    empty_int32_slice()
150                } else {
151                    llr_Expression::ReadLocalVariable {
152                        name: SmolStr::new_static("repeated_indices"),
153                        ty: Type::Array(Type::Int32.into()),
154                    }
155                },
156                if input_data.compute_cells.is_none() {
157                    empty_int32_slice()
158                } else {
159                    llr_Expression::ReadLocalVariable {
160                        name: SmolStr::new_static("repeater_steps"),
161                        ty: Type::Array(Type::Int32.into()),
162                    }
163                },
164            ],
165            return_ty: Type::Array(Type::Int32.into()),
166        };
167        if let Some((cells_variable, elements)) = input_data.compute_cells {
168            llr_Expression::WithGridInputData {
169                cells_variable,
170                repeater_indices_var_name: SmolStr::new_static("repeated_indices"),
171                repeater_steps_var_name: SmolStr::new_static("repeater_steps"),
172                elements,
173                sub_expression: Box::new(sub_expression),
174            }
175        } else {
176            sub_expression
177        }
178    }
179}
180
181pub(super) fn solve_grid_layout(
182    layout_organized_data_prop: &NamedReference,
183    layout: &crate::layout::GridLayout,
184    o: Orientation,
185    ctx: &mut ExpressionLoweringCtx,
186) -> llr_Expression {
187    let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
188    let cells = ctx.map_property_reference(layout_organized_data_prop);
189    let size = layout_geometry_size(&layout.geometry.rect, o, ctx);
190    let orientation_expr = llr_Expression::EnumerationValue(EnumerationValue {
191        value: o as _,
192        enumeration: crate::typeregister::BUILTIN.with(|b| b.enums.Orientation.clone()),
193    });
194    let data = make_struct(
195        BuiltinPrivateStruct::GridLayoutData,
196        [
197            ("size", Type::Float32, size),
198            ("spacing", Type::Float32, spacing),
199            ("padding", padding.ty(ctx), padding),
200            ("organized_data", Type::ArrayOfU16, llr_Expression::PropertyReference(cells)),
201        ],
202    );
203    let constraints_result = grid_layout_cell_constraints(layout, o, ctx);
204
205    match constraints_result.compute_cells {
206        Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
207            cells_variable: cells_variable.clone(),
208            repeater_indices_var_name: Some("repeated_indices".into()),
209            repeater_steps_var_name: Some("repeater_steps".into()),
210            elements,
211            orientation: o,
212            sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
213                function: "solve_grid_layout".into(),
214                arguments: vec![
215                    data,
216                    llr_Expression::ReadLocalVariable {
217                        name: cells_variable.into(),
218                        ty: constraints_result.cells.ty(ctx),
219                    },
220                    orientation_expr,
221                    llr_Expression::ReadLocalVariable {
222                        name: "repeated_indices".into(),
223                        ty: Type::Array(Type::Int32.into()),
224                    },
225                    llr_Expression::ReadLocalVariable {
226                        name: "repeater_steps".into(),
227                        ty: Type::Array(Type::Int32.into()),
228                    },
229                ],
230                return_ty: Type::LayoutCache,
231            }),
232        },
233        None => llr_Expression::ExtraBuiltinFunctionCall {
234            function: "solve_grid_layout".into(),
235            arguments: vec![
236                data,
237                constraints_result.cells,
238                orientation_expr,
239                empty_int32_slice(),
240                empty_int32_slice(),
241            ],
242            return_ty: Type::LayoutCache,
243        },
244    }
245}
246
247pub(super) fn solve_box_layout(
248    layout: &crate::layout::BoxLayout,
249    o: Orientation,
250    ctx: &mut ExpressionLoweringCtx,
251) -> llr_Expression {
252    let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
253    let bld = box_layout_data(layout, o, ctx);
254    let size = layout_geometry_size(&layout.geometry.rect, o, ctx);
255    let data = make_struct(
256        BuiltinPrivateStruct::BoxLayoutData,
257        [
258            ("size", Type::Float32, size),
259            ("spacing", Type::Float32, spacing),
260            ("padding", padding.ty(ctx), padding),
261            (
262                "alignment",
263                crate::typeregister::BUILTIN
264                    .with(|e| Type::Enumeration(e.enums.LayoutAlignment.clone())),
265                bld.alignment,
266            ),
267            ("cells", bld.cells.ty(ctx), bld.cells),
268        ],
269    );
270    match bld.compute_cells {
271        Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
272            cells_variable,
273            repeater_indices_var_name: Some("repeated_indices".into()),
274            repeater_steps_var_name: None,
275            elements,
276            orientation: o,
277            sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
278                function: "solve_box_layout".into(),
279                arguments: vec![
280                    data,
281                    llr_Expression::ReadLocalVariable {
282                        name: "repeated_indices".into(),
283                        ty: Type::Array(Type::Int32.into()),
284                    },
285                ],
286                return_ty: Type::LayoutCache,
287            }),
288        },
289        None => llr_Expression::ExtraBuiltinFunctionCall {
290            function: "solve_box_layout".into(),
291            arguments: vec![data, empty_int32_slice()],
292            return_ty: Type::LayoutCache,
293        },
294    }
295}
296
297pub(super) fn solve_flexbox_layout(
298    layout: &crate::layout::FlexboxLayout,
299    ctx: &mut ExpressionLoweringCtx,
300) -> llr_Expression {
301    let (padding_h, spacing_h) =
302        generate_layout_padding_and_spacing(&layout.geometry, Orientation::Horizontal, ctx);
303    let (padding_v, spacing_v) =
304        generate_layout_padding_and_spacing(&layout.geometry, Orientation::Vertical, ctx);
305    let fld = flexbox_layout_data(layout, ctx, CellsVConstraint::ContainerWidth);
306    let width = layout_geometry_size(&layout.geometry.rect, Orientation::Horizontal, ctx);
307    let height = layout_geometry_size(&layout.geometry.rect, Orientation::Vertical, ctx);
308    let data = make_struct(
309        BuiltinPrivateStruct::FlexboxLayoutData,
310        [
311            ("width", Type::Float32, width),
312            ("height", Type::Float32, height),
313            ("spacing_h", Type::Float32, spacing_h),
314            ("spacing_v", Type::Float32, spacing_v),
315            ("padding_h", padding_h.ty(ctx), padding_h),
316            ("padding_v", padding_v.ty(ctx), padding_v),
317            (
318                "alignment",
319                crate::typeregister::BUILTIN
320                    .with(|e| Type::Enumeration(e.enums.LayoutAlignment.clone())),
321                fld.alignment,
322            ),
323            (
324                "direction",
325                crate::typeregister::BUILTIN
326                    .with(|e| Type::Enumeration(e.enums.FlexboxLayoutDirection.clone())),
327                fld.direction,
328            ),
329            (
330                "align_content",
331                crate::typeregister::BUILTIN
332                    .with(|e| Type::Enumeration(e.enums.FlexboxLayoutAlignContent.clone())),
333                fld.align_content,
334            ),
335            (
336                "align_items",
337                crate::typeregister::BUILTIN
338                    .with(|e| Type::Enumeration(e.enums.FlexboxLayoutAlignItems.clone())),
339                fld.align_items,
340            ),
341            (
342                "flex_wrap",
343                crate::typeregister::BUILTIN
344                    .with(|e| Type::Enumeration(e.enums.FlexboxLayoutWrap.clone())),
345                fld.flex_wrap,
346            ),
347            ("cells_h", fld.cells_h.ty(ctx), fld.cells_h),
348            ("cells_v", fld.cells_v.ty(ctx), fld.cells_v),
349        ],
350    );
351    match fld.compute_cells {
352        Some((cells_h_var, cells_v_var, elements)) => llr_Expression::WithFlexboxLayoutItemInfo {
353            cells_h_variable: cells_h_var,
354            cells_v_variable: cells_v_var,
355            repeater_indices_var_name: Some("repeated_indices".into()),
356            elements,
357            sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
358                function: "solve_flexbox_layout".into(),
359                arguments: vec![
360                    data,
361                    llr_Expression::ReadLocalVariable {
362                        name: "repeated_indices".into(),
363                        ty: Type::Array(Type::Int32.into()),
364                    },
365                ],
366                return_ty: Type::LayoutCache,
367            }),
368        },
369        None => llr_Expression::ExtraBuiltinFunctionCall {
370            function: "solve_flexbox_layout".into(),
371            arguments: vec![data, empty_int32_slice()],
372            return_ty: Type::LayoutCache,
373        },
374    }
375}
376
377pub(super) fn compute_flexbox_layout_info(
378    layout: &crate::layout::FlexboxLayout,
379    orientation: Orientation,
380    ctx: &mut ExpressionLoweringCtx,
381) -> llr_Expression {
382    let fld = flexbox_layout_data(layout, ctx, CellsVConstraint::ItemPreferredWidth);
383
384    match layout.axis_relation(orientation) {
385        crate::layout::FlexboxAxisRelation::MainAxis => {
386            compute_flexbox_layout_info_for_direction(layout, orientation, false, fld, ctx)
387        }
388        crate::layout::FlexboxAxisRelation::CrossAxis => {
389            compute_flexbox_layout_info_for_direction(layout, orientation, true, fld, ctx)
390        }
391        crate::layout::FlexboxAxisRelation::Unknown => {
392            // Direction is not known at compile time - generate runtime conditional
393            // This ensures we only read the constraint (width/height) in the branch where it's needed
394            let row_expr = compute_flexbox_layout_info_for_direction(
395                layout,
396                orientation,
397                orientation == Orientation::Vertical, // cross-axis if orientation is vertical
398                fld.clone(),
399                ctx,
400            );
401            let col_expr = compute_flexbox_layout_info_for_direction(
402                layout,
403                orientation,
404                orientation == Orientation::Horizontal, // cross-axis if orientation is horizontal
405                fld,
406                ctx,
407            );
408
409            // Condition: direction == Row || direction == RowReverse
410            let direction_enum =
411                crate::typeregister::BUILTIN.with(|e| e.enums.FlexboxLayoutDirection.clone());
412            let direction_ref = llr_Expression::PropertyReference(
413                ctx.map_property_reference(layout.direction.as_ref().unwrap()),
414            );
415
416            let is_row_condition = llr_Expression::BinaryExpression {
417                lhs: Box::new(llr_Expression::BinaryExpression {
418                    lhs: Box::new(direction_ref.clone()),
419                    rhs: Box::new(llr_Expression::EnumerationValue(EnumerationValue {
420                        value: 0, // FlexboxLayoutDirection::Row
421                        enumeration: direction_enum.clone(),
422                    })),
423                    op: '=',
424                }),
425                rhs: Box::new(llr_Expression::BinaryExpression {
426                    lhs: Box::new(direction_ref),
427                    rhs: Box::new(llr_Expression::EnumerationValue(EnumerationValue {
428                        value: 1, // FlexboxLayoutDirection::RowReverse
429                        enumeration: direction_enum,
430                    })),
431                    op: '=',
432                }),
433                op: '|',
434            };
435
436            llr_Expression::Condition {
437                condition: Box::new(is_row_condition),
438                true_expr: Box::new(row_expr),
439                false_expr: Box::new(col_expr),
440            }
441        }
442    }
443}
444
445fn compute_flexbox_layout_info_for_direction(
446    layout: &crate::layout::FlexboxLayout,
447    orientation: Orientation,
448    is_cross_axis: bool,
449    fld: FlexboxLayoutDataResult,
450    ctx: &mut ExpressionLoweringCtx,
451) -> llr_Expression {
452    let (padding_h, spacing_h) =
453        generate_layout_padding_and_spacing(&layout.geometry, Orientation::Horizontal, ctx);
454    let (padding_v, spacing_v) =
455        generate_layout_padding_and_spacing(&layout.geometry, Orientation::Vertical, ctx);
456
457    if is_cross_axis {
458        // Cross-axis layout info: pass the main-axis container dimension as constraint
459        // for accurate wrapping. Falls back to heuristic if the constraint is unavailable
460        // (e.g., due to circular dependency in nested perpendicular flexboxes).
461        let constraint_size = match orientation {
462            Orientation::Horizontal => {
463                layout_geometry_size(&layout.geometry.rect, Orientation::Vertical, ctx)
464            }
465            Orientation::Vertical => {
466                layout_geometry_size(&layout.geometry.rect, Orientation::Horizontal, ctx)
467            }
468        };
469
470        let arguments = vec![
471            fld.cells_h,
472            fld.cells_v,
473            spacing_h,
474            spacing_v,
475            padding_h,
476            padding_v,
477            fld.direction,
478            fld.flex_wrap,
479            constraint_size,
480        ];
481
482        match fld.compute_cells {
483            Some((cells_h_var, cells_v_var, elements)) => {
484                llr_Expression::WithFlexboxLayoutItemInfo {
485                    cells_h_variable: cells_h_var,
486                    cells_v_variable: cells_v_var,
487                    repeater_indices_var_name: None,
488                    elements,
489                    sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
490                        function: "flexbox_layout_info_cross_axis".into(),
491                        arguments,
492                        return_ty: crate::typeregister::layout_info_type().into(),
493                    }),
494                }
495            }
496            None => llr_Expression::ExtraBuiltinFunctionCall {
497                function: "flexbox_layout_info_cross_axis".into(),
498                arguments,
499                return_ty: crate::typeregister::layout_info_type().into(),
500            },
501        }
502    } else {
503        // Main axis: only needs same-axis cells, avoiding cross-axis binding loop.
504        let (cells, spacing, padding) = match orientation {
505            Orientation::Horizontal => (fld.cells_h, spacing_h, padding_h),
506            Orientation::Vertical => (fld.cells_v, spacing_v, padding_v),
507        };
508
509        match fld.compute_cells {
510            Some((cells_h_var, cells_v_var, elements)) => {
511                let cells_var = match orientation {
512                    Orientation::Horizontal => cells_h_var.clone(),
513                    Orientation::Vertical => cells_v_var.clone(),
514                };
515                llr_Expression::WithFlexboxLayoutItemInfo {
516                    cells_h_variable: cells_h_var,
517                    cells_v_variable: cells_v_var,
518                    repeater_indices_var_name: None,
519                    elements,
520                    sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
521                        function: "flexbox_layout_info_main_axis".into(),
522                        arguments: vec![
523                            llr_Expression::ReadLocalVariable {
524                                name: cells_var.into(),
525                                ty: Type::Array(Rc::new(
526                                    crate::typeregister::flexbox_layout_item_info_type(),
527                                )),
528                            },
529                            spacing,
530                            padding,
531                            fld.flex_wrap,
532                        ],
533                        return_ty: crate::typeregister::layout_info_type().into(),
534                    }),
535                }
536            }
537            None => llr_Expression::ExtraBuiltinFunctionCall {
538                function: "flexbox_layout_info_main_axis".into(),
539                arguments: vec![cells, spacing, padding, fld.flex_wrap],
540                return_ty: crate::typeregister::layout_info_type().into(),
541            },
542        }
543    }
544}
545
546#[derive(Clone)]
547struct FlexboxLayoutDataResult {
548    alignment: llr_Expression,
549    direction: llr_Expression,
550    align_content: llr_Expression,
551    align_items: llr_Expression,
552    flex_wrap: llr_Expression,
553    cells_h: llr_Expression,
554    cells_v: llr_Expression,
555    /// When there are repeaters involved, we need to do a WithFlexboxLayoutItemInfo with the
556    /// given cells_h/cells_v variable names and elements (each static element has a tuple of (h, v) layout info)
557    compute_cells: Option<(
558        String,
559        String,
560        Vec<Either<(llr_Expression, llr_Expression), LayoutRepeatedElement>>,
561    )>,
562}
563
564/// Controls how cells_v width constraint is determined for column-direction flex.
565enum CellsVConstraint {
566    /// Use the item's own preferred width (safe for ComputeFlexboxLayoutInfo,
567    /// avoids cycle when parent queries the flex's preferred width).
568    ItemPreferredWidth,
569    /// Use the container's width (accurate for SolveFlexboxLayout, since the
570    /// container width is already set by the parent at solve time).
571    ContainerWidth,
572}
573
574fn flexbox_layout_data(
575    layout: &crate::layout::FlexboxLayout,
576    ctx: &mut ExpressionLoweringCtx,
577    cells_v_constraint: CellsVConstraint,
578) -> FlexboxLayoutDataResult {
579    let alignment = if let Some(expr) = &layout.geometry.alignment {
580        llr_Expression::PropertyReference(ctx.map_property_reference(expr))
581    } else {
582        let e = crate::typeregister::BUILTIN.with(|e| e.enums.LayoutAlignment.clone());
583        llr_Expression::EnumerationValue(EnumerationValue {
584            value: e.default_value,
585            enumeration: e,
586        })
587    };
588
589    let direction = if let Some(expr) = &layout.direction {
590        llr_Expression::PropertyReference(ctx.map_property_reference(expr))
591    } else {
592        let e = crate::typeregister::BUILTIN.with(|e| e.enums.FlexboxLayoutDirection.clone());
593        llr_Expression::EnumerationValue(EnumerationValue {
594            value: e.default_value,
595            enumeration: e,
596        })
597    };
598
599    let align_content = if let Some(expr) = &layout.align_content {
600        llr_Expression::PropertyReference(ctx.map_property_reference(expr))
601    } else {
602        let e = crate::typeregister::BUILTIN.with(|e| e.enums.FlexboxLayoutAlignContent.clone());
603        llr_Expression::EnumerationValue(EnumerationValue {
604            value: e.default_value,
605            enumeration: e,
606        })
607    };
608
609    let align_items = if let Some(expr) = &layout.align_items {
610        llr_Expression::PropertyReference(ctx.map_property_reference(expr))
611    } else {
612        let e = crate::typeregister::BUILTIN.with(|e| e.enums.FlexboxLayoutAlignItems.clone());
613        llr_Expression::EnumerationValue(EnumerationValue {
614            value: e.default_value,
615            enumeration: e,
616        })
617    };
618
619    let flex_wrap = if let Some(expr) = &layout.flex_wrap {
620        llr_Expression::PropertyReference(ctx.map_property_reference(expr))
621    } else {
622        let e = crate::typeregister::BUILTIN.with(|e| e.enums.FlexboxLayoutWrap.clone());
623        llr_Expression::EnumerationValue(EnumerationValue {
624            value: e.default_value,
625            enumeration: e,
626        })
627    };
628
629    let repeater_count =
630        layout.elems.iter().filter(|i| i.item.element.borrow().repeated.is_some()).count();
631
632    let element_ty = crate::typeregister::flexbox_layout_item_info_type();
633
634    let flex_prop =
635        |li: &crate::layout::FlexboxLayoutItem, ctx: &mut ExpressionLoweringCtx| -> FlexItemProps {
636            FlexItemProps {
637                grow: li
638                    .flex_grow
639                    .as_ref()
640                    .map(|nr| llr_Expression::PropertyReference(ctx.map_property_reference(nr)))
641                    .unwrap_or(llr_Expression::NumberLiteral(0.0)),
642                shrink: li
643                    .flex_shrink
644                    .as_ref()
645                    .map(|nr| llr_Expression::PropertyReference(ctx.map_property_reference(nr)))
646                    .unwrap_or(llr_Expression::NumberLiteral(0.0)),
647                basis: li
648                    .flex_basis
649                    .as_ref()
650                    .map(|nr| llr_Expression::PropertyReference(ctx.map_property_reference(nr)))
651                    .unwrap_or(llr_Expression::NumberLiteral(-1.0)),
652                align_self: li
653                    .align_self
654                    .as_ref()
655                    .map(|nr| llr_Expression::PropertyReference(ctx.map_property_reference(nr)))
656                    .unwrap_or(default_align_self().1),
657                order: li
658                    .order
659                    .as_ref()
660                    .map(|nr| llr_Expression::PropertyReference(ctx.map_property_reference(nr)))
661                    .unwrap_or(llr_Expression::NumberLiteral(0.0)),
662            }
663        };
664
665    let use_container_width = matches!(cells_v_constraint, CellsVConstraint::ContainerWidth)
666        && matches!(
667            layout.axis_relation(Orientation::Vertical),
668            crate::layout::FlexboxAxisRelation::MainAxis
669        );
670
671    if repeater_count == 0 {
672        let cells_h = llr_Expression::Array {
673            values: layout
674                .elems
675                .iter()
676                .map(|li| {
677                    let layout_info_h = get_layout_info(
678                        &li.item.element,
679                        ctx,
680                        &li.item.constraints,
681                        Orientation::Horizontal,
682                    );
683                    let flex_props = flex_prop(li, ctx);
684                    make_flexbox_cell_data_struct(layout_info_h, flex_props)
685                })
686                .collect(),
687            element_ty: element_ty.clone(),
688            output: llr_ArrayOutput::Slice,
689        };
690        // For cells_v, pass a width constraint for items that need
691        // height-for-width (Text with word-wrap, Image with aspect ratio).
692        // In column direction, items get stretched to the container's width,
693        // so use that as the constraint. Otherwise use the item's own
694        // preferred horizontal size.
695        let cells_v = llr_Expression::Array {
696            values: layout
697                .elems
698                .iter()
699                .map(|li| {
700                    let constraint = if use_container_width {
701                        cross_axis_constraint_expr(&li.item.element).and_then(|_| {
702                            layout.geometry.rect.width_reference.as_ref().map(|nr| {
703                                crate::expression_tree::Expression::PropertyReference(nr.clone())
704                            })
705                        })
706                    } else {
707                        cross_axis_constraint_expr(&li.item.element)
708                    };
709                    let layout_info_v = get_layout_info_with_constraint(
710                        &li.item.element,
711                        ctx,
712                        &li.item.constraints,
713                        Orientation::Vertical,
714                        constraint,
715                    );
716                    let flex_props = flex_prop(li, ctx);
717                    make_flexbox_cell_data_struct(layout_info_v, flex_props)
718                })
719                .collect(),
720            element_ty,
721            output: llr_ArrayOutput::Slice,
722        };
723        FlexboxLayoutDataResult {
724            alignment,
725            direction,
726            align_content,
727            align_items,
728            flex_wrap,
729            cells_h,
730            cells_v,
731            compute_cells: None,
732        }
733    } else {
734        let mut elements = Vec::new();
735        for item in &layout.elems {
736            if item.item.element.borrow().repeated.is_some() {
737                let repeater_index = match ctx
738                    .mapping
739                    .element_mapping
740                    .get(&item.item.element.clone().into())
741                    .unwrap()
742                {
743                    LoweredElement::Repeated { repeated_index } => *repeated_index,
744                    _ => panic!(),
745                };
746                elements.push(Either::Right(LayoutRepeatedElement {
747                    repeater_index,
748                    row_child_templates: None,
749                }))
750            } else {
751                // For static elements, we need both orientations
752                let layout_info_h = get_layout_info(
753                    &item.item.element,
754                    ctx,
755                    &item.item.constraints,
756                    Orientation::Horizontal,
757                );
758                let constraint = if use_container_width {
759                    cross_axis_constraint_expr(&item.item.element).and_then(|_| {
760                        layout.geometry.rect.width_reference.as_ref().map(|nr| {
761                            crate::expression_tree::Expression::PropertyReference(nr.clone())
762                        })
763                    })
764                } else {
765                    cross_axis_constraint_expr(&item.item.element)
766                };
767                let layout_info_v = get_layout_info_with_constraint(
768                    &item.item.element,
769                    ctx,
770                    &item.item.constraints,
771                    Orientation::Vertical,
772                    constraint,
773                );
774                let flex_props = flex_prop(item, ctx);
775                elements.push(Either::Left((
776                    make_flexbox_cell_data_struct(layout_info_h, flex_props.clone()),
777                    make_flexbox_cell_data_struct(layout_info_v, flex_props),
778                )));
779            }
780        }
781        let cells_h = llr_Expression::ReadLocalVariable {
782            name: "cells_h".into(),
783            ty: Type::Array(Rc::new(crate::typeregister::flexbox_layout_item_info_type())),
784        };
785        let cells_v = llr_Expression::ReadLocalVariable {
786            name: "cells_v".into(),
787            ty: Type::Array(Rc::new(crate::typeregister::flexbox_layout_item_info_type())),
788        };
789        FlexboxLayoutDataResult {
790            alignment,
791            direction,
792            align_content,
793            align_items,
794            flex_wrap,
795            cells_h,
796            cells_v,
797            compute_cells: Some(("cells_h".into(), "cells_v".into(), elements)),
798        }
799    }
800}
801
802struct BoxLayoutDataResult {
803    alignment: llr_Expression,
804    cells: llr_Expression,
805    /// When there are repeater involved, we need to do a WithLayoutItemInfo with the
806    /// given cell variable and elements
807    compute_cells: Option<(String, Vec<Either<llr_Expression, LayoutRepeatedElement>>)>,
808}
809
810fn default_align_self() -> (Type, llr_Expression) {
811    let e = crate::typeregister::BUILTIN.with(|e| e.enums.FlexboxLayoutAlignSelf.clone());
812    (
813        Type::Enumeration(e.clone()),
814        llr_Expression::EnumerationValue(EnumerationValue {
815            value: e.default_value,
816            enumeration: e,
817        }),
818    )
819}
820
821fn make_layout_cell_data_struct(layout_info: llr_Expression) -> llr_Expression {
822    make_struct(
823        BuiltinPrivateStruct::LayoutItemInfo,
824        [("constraint", crate::typeregister::layout_info_type().into(), layout_info)],
825    )
826}
827
828#[derive(Clone)]
829struct FlexItemProps {
830    grow: llr_Expression,
831    shrink: llr_Expression,
832    basis: llr_Expression,
833    align_self: llr_Expression,
834    order: llr_Expression,
835}
836
837fn make_flexbox_cell_data_struct(layout_info: llr_Expression, fp: FlexItemProps) -> llr_Expression {
838    let (align_self_ty, _) = default_align_self();
839    make_struct(
840        BuiltinPrivateStruct::FlexboxLayoutItemInfo,
841        [
842            ("constraint", crate::typeregister::layout_info_type().into(), layout_info),
843            ("flex-grow", Type::Float32, fp.grow),
844            ("flex-shrink", Type::Float32, fp.shrink),
845            ("flex-basis", Type::Float32, fp.basis),
846            ("flex-align-self", align_self_ty, fp.align_self),
847            ("flex-order", Type::Int32, fp.order),
848        ],
849    )
850}
851
852fn box_layout_data(
853    layout: &crate::layout::BoxLayout,
854    orientation: Orientation,
855    ctx: &mut ExpressionLoweringCtx,
856) -> BoxLayoutDataResult {
857    let alignment = if let Some(expr) = &layout.geometry.alignment {
858        llr_Expression::PropertyReference(ctx.map_property_reference(expr))
859    } else {
860        let e = crate::typeregister::BUILTIN.with(|e| e.enums.LayoutAlignment.clone());
861        llr_Expression::EnumerationValue(EnumerationValue {
862            value: e.default_value,
863            enumeration: e,
864        })
865    };
866
867    let repeater_count =
868        layout.elems.iter().filter(|i| i.element.borrow().repeated.is_some()).count();
869
870    let element_ty = crate::typeregister::layout_item_info_type();
871
872    if repeater_count == 0 {
873        let cells = llr_Expression::Array {
874            values: layout
875                .elems
876                .iter()
877                .map(|li| {
878                    let layout_info =
879                        get_layout_info(&li.element, ctx, &li.constraints, orientation);
880                    make_layout_cell_data_struct(layout_info)
881                })
882                .collect(),
883            element_ty,
884            output: llr_ArrayOutput::Slice,
885        };
886        BoxLayoutDataResult { alignment, cells, compute_cells: None }
887    } else {
888        let mut elements = Vec::new();
889        for item in &layout.elems {
890            if item.element.borrow().repeated.is_some() {
891                let repeater_index =
892                    match ctx.mapping.element_mapping.get(&item.element.clone().into()).unwrap() {
893                        LoweredElement::Repeated { repeated_index } => *repeated_index,
894                        _ => panic!(),
895                    };
896                elements.push(Either::Right(LayoutRepeatedElement {
897                    repeater_index,
898                    row_child_templates: None,
899                }))
900            } else {
901                let layout_info =
902                    get_layout_info(&item.element, ctx, &item.constraints, orientation);
903                elements.push(Either::Left(make_layout_cell_data_struct(layout_info)));
904            }
905        }
906        let cells = llr_Expression::ReadLocalVariable {
907            name: "cells".into(),
908            ty: Type::Array(Rc::new(crate::typeregister::layout_info_type().into())),
909        };
910        BoxLayoutDataResult { alignment, cells, compute_cells: Some(("cells".into(), elements)) }
911    }
912}
913
914struct GridLayoutCellConstraintsResult {
915    cells: llr_Expression,
916    /// When there are repeater involved, we need to do a WithLayoutItemInfo with the
917    /// given cell variable and elements
918    compute_cells: Option<(String, Vec<Either<llr_Expression, LayoutRepeatedElement>>)>,
919}
920
921fn grid_layout_cell_constraints(
922    layout: &crate::layout::GridLayout,
923    orientation: Orientation,
924    ctx: &mut ExpressionLoweringCtx,
925) -> GridLayoutCellConstraintsResult {
926    let repeater_count =
927        layout.elems.iter().filter(|i| i.item.element.borrow().repeated.is_some()).count();
928
929    let element_ty = crate::typeregister::layout_item_info_type();
930
931    if repeater_count == 0 {
932        let cells = llr_Expression::Array {
933            element_ty,
934            values: layout
935                .elems
936                .iter()
937                .map(|li| {
938                    let layout_info =
939                        get_layout_info(&li.item.element, ctx, &li.item.constraints, orientation);
940                    make_layout_cell_data_struct(layout_info)
941                })
942                .collect(),
943            output: llr_ArrayOutput::Slice,
944        };
945        GridLayoutCellConstraintsResult { cells, compute_cells: None }
946    } else {
947        let mut elements = Vec::new();
948        for item in &layout.elems {
949            if item.item.element.borrow().repeated.is_some() {
950                let repeater_index = match ctx
951                    .mapping
952                    .element_mapping
953                    .get(&item.item.element.clone().into())
954                    .unwrap()
955                {
956                    LoweredElement::Repeated { repeated_index } => *repeated_index,
957                    _ => panic!(),
958                };
959                let row_child_templates = get_row_child_templates(&item.item.element, ctx);
960                elements.push(Either::Right(LayoutRepeatedElement {
961                    repeater_index,
962                    row_child_templates,
963                }));
964            } else {
965                let layout_info =
966                    get_layout_info(&item.item.element, ctx, &item.item.constraints, orientation);
967                elements.push(Either::Left(make_layout_cell_data_struct(layout_info)));
968            }
969        }
970        let cells = llr_Expression::ReadLocalVariable {
971            name: "cells".into(),
972            ty: Type::Array(Rc::new(crate::typeregister::layout_info_type().into())),
973        };
974        GridLayoutCellConstraintsResult { cells, compute_cells: Some(("cells".into(), elements)) }
975    }
976}
977
978struct GridLayoutInputDataResult {
979    cells: llr_Expression,
980    /// When there are repeaters involved, we need to do a WithGridInputData with the
981    /// given cell variable and elements
982    compute_cells: Option<(String, Vec<Either<llr_Expression, GridLayoutRepeatedElement>>)>,
983}
984
985// helper for organize_grid_layout()
986fn grid_layout_input_data(
987    layout: &crate::layout::GridLayout,
988    ctx: &mut ExpressionLoweringCtx,
989) -> GridLayoutInputDataResult {
990    let propref = |named_ref: &RowColExpr| match named_ref {
991        RowColExpr::Literal(n) => llr_Expression::NumberLiteral((*n).into()),
992        RowColExpr::Named(nr) => llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
993        RowColExpr::Auto => llr_Expression::NumberLiteral(i_slint_common::ROW_COL_AUTO as _),
994    };
995    let input_data_for_cell = |elem: &crate::layout::GridLayoutElement,
996                               new_row_expr: llr_Expression| {
997        let row_expr = propref(&elem.cell.borrow().row_expr);
998        let col_expr = propref(&elem.cell.borrow().col_expr);
999        let rowspan_expr = propref(&elem.cell.borrow().rowspan_expr);
1000        let colspan_expr = propref(&elem.cell.borrow().colspan_expr);
1001
1002        make_struct(
1003            BuiltinPrivateStruct::GridLayoutInputData,
1004            [
1005                ("new_row", Type::Bool, new_row_expr),
1006                ("row", Type::Float32, row_expr),
1007                ("col", Type::Float32, col_expr),
1008                ("rowspan", Type::Float32, rowspan_expr),
1009                ("colspan", Type::Float32, colspan_expr),
1010            ],
1011        )
1012    };
1013    let repeater_count =
1014        layout.elems.iter().filter(|i| i.item.element.borrow().repeated.is_some()).count();
1015
1016    let element_ty = grid_layout_input_data_ty();
1017
1018    if repeater_count == 0 {
1019        let cells = llr_Expression::Array {
1020            element_ty,
1021            values: layout
1022                .elems
1023                .iter()
1024                .map(|elem| {
1025                    input_data_for_cell(
1026                        elem,
1027                        llr_Expression::BoolLiteral(elem.cell.borrow().new_row),
1028                    )
1029                })
1030                .collect(),
1031            output: llr_ArrayOutput::Slice,
1032        };
1033        GridLayoutInputDataResult { cells, compute_cells: None }
1034    } else {
1035        let mut elements = Vec::new();
1036        let mut after_repeater_in_same_row = false;
1037        for item in &layout.elems {
1038            let new_row = item.cell.borrow().new_row;
1039            if new_row {
1040                after_repeater_in_same_row = false;
1041            }
1042            if item.item.element.borrow().repeated.is_some() {
1043                let repeater_index = match ctx
1044                    .mapping
1045                    .element_mapping
1046                    .get(&item.item.element.clone().into())
1047                    .unwrap()
1048                {
1049                    LoweredElement::Repeated { repeated_index } => *repeated_index,
1050                    _ => panic!(),
1051                };
1052                let row_child_templates = get_row_child_templates(&item.item.element, ctx);
1053                let repeated_element =
1054                    GridLayoutRepeatedElement { new_row, repeater_index, row_child_templates };
1055                elements.push(Either::Right(repeated_element));
1056                after_repeater_in_same_row = true;
1057            } else {
1058                let new_row_expr = if new_row || !after_repeater_in_same_row {
1059                    llr_Expression::BoolLiteral(new_row)
1060                } else {
1061                    llr_Expression::ReadLocalVariable {
1062                        name: SmolStr::new_static("new_row"),
1063                        ty: Type::Bool,
1064                    }
1065                };
1066                elements.push(Either::Left(input_data_for_cell(item, new_row_expr)));
1067            }
1068        }
1069        let cells = llr_Expression::ReadLocalVariable {
1070            name: "cells".into(),
1071            ty: Type::Array(Rc::new(element_ty)),
1072        };
1073        GridLayoutInputDataResult { cells, compute_cells: Some(("cells".into(), elements)) }
1074    }
1075}
1076
1077pub(super) fn grid_layout_input_data_ty() -> Type {
1078    Type::Struct(Rc::new(Struct {
1079        fields: IntoIterator::into_iter([
1080            (SmolStr::new_static("new_row"), Type::Bool),
1081            (SmolStr::new_static("row"), Type::Int32),
1082            (SmolStr::new_static("col"), Type::Int32),
1083            (SmolStr::new_static("rowspan"), Type::Int32),
1084            (SmolStr::new_static("colspan"), Type::Int32),
1085        ])
1086        .collect(),
1087        name: BuiltinPrivateStruct::GridLayoutInputData.into(),
1088    }))
1089}
1090
1091fn generate_layout_padding_and_spacing(
1092    layout_geometry: &crate::layout::LayoutGeometry,
1093    orientation: Orientation,
1094    ctx: &ExpressionLoweringCtx,
1095) -> (llr_Expression, llr_Expression) {
1096    let padding_prop = |expr| {
1097        if let Some(expr) = expr {
1098            llr_Expression::PropertyReference(ctx.map_property_reference(expr))
1099        } else {
1100            llr_Expression::NumberLiteral(0.)
1101        }
1102    };
1103    let spacing = padding_prop(layout_geometry.spacing.orientation(orientation));
1104    let (begin, end) = layout_geometry.padding.begin_end(orientation);
1105
1106    let padding = make_struct(
1107        BuiltinPrivateStruct::Padding,
1108        [("begin", Type::Float32, padding_prop(begin)), ("end", Type::Float32, padding_prop(end))],
1109    );
1110
1111    (padding, spacing)
1112}
1113
1114/// For elements that benefit from a cross-axis constraint (Text with word-wrap,
1115/// Image with aspect ratio), returns the horizontal preferred size expression
1116/// to use as the constraint. Returns None for components with layout_info_prop
1117/// (which would create cycles) and items with hardcoded layout info.
1118fn cross_axis_constraint_expr(elem: &ElementRc) -> Option<crate::expression_tree::Expression> {
1119    if elem.borrow().layout_info_prop(Orientation::Vertical).is_some() {
1120        return None;
1121    }
1122    if !matches!(
1123        crate::layout::implicit_layout_info_call(
1124            elem,
1125            Orientation::Vertical,
1126            crate::layout::BuiltinFilter::All,
1127        ),
1128        Some(crate::expression_tree::Expression::FunctionCall { .. })
1129    ) {
1130        return None;
1131    }
1132    crate::layout::implicit_layout_info_call(
1133        elem,
1134        Orientation::Horizontal,
1135        crate::layout::BuiltinFilter::All,
1136    )
1137    .map(|expr| crate::expression_tree::Expression::StructFieldAccess {
1138        base: Box::new(expr),
1139        name: "preferred".into(),
1140    })
1141}
1142
1143fn layout_geometry_size(
1144    rect: &crate::layout::LayoutRect,
1145    orientation: Orientation,
1146    ctx: &ExpressionLoweringCtx,
1147) -> llr_Expression {
1148    match rect.size_reference(orientation) {
1149        Some(nr) => llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
1150        None => llr_Expression::NumberLiteral(0.),
1151    }
1152}
1153
1154pub fn get_layout_info(
1155    elem: &ElementRc,
1156    ctx: &mut ExpressionLoweringCtx,
1157    constraints: &crate::layout::LayoutConstraints,
1158    orientation: Orientation,
1159) -> llr_Expression {
1160    get_layout_info_with_constraint(elem, ctx, constraints, orientation, None)
1161}
1162
1163fn get_layout_info_with_constraint(
1164    elem: &ElementRc,
1165    ctx: &mut ExpressionLoweringCtx,
1166    constraints: &crate::layout::LayoutConstraints,
1167    orientation: Orientation,
1168    constraint: Option<crate::expression_tree::Expression>,
1169) -> llr_Expression {
1170    let layout_info = if let Some(layout_info_prop) = &elem.borrow().layout_info_prop(orientation) {
1171        llr_Expression::PropertyReference(ctx.map_property_reference(layout_info_prop))
1172    } else {
1173        super::lower_expression::lower_expression(
1174            &crate::layout::implicit_layout_info_call_with_constraint(
1175                elem,
1176                orientation,
1177                crate::layout::BuiltinFilter::All,
1178                constraint,
1179            )
1180            .unwrap(),
1181            ctx,
1182        )
1183    };
1184
1185    if constraints.has_explicit_restrictions(orientation) {
1186        let store = llr_Expression::StoreLocalVariable {
1187            name: "layout_info".into(),
1188            value: layout_info.into(),
1189        };
1190        let ty = crate::typeregister::layout_info_type();
1191        let mut values = ty
1192            .fields
1193            .keys()
1194            .map(|p| {
1195                (
1196                    p.clone(),
1197                    llr_Expression::StructFieldAccess {
1198                        base: llr_Expression::ReadLocalVariable {
1199                            name: "layout_info".into(),
1200                            ty: ty.clone().into(),
1201                        }
1202                        .into(),
1203                        name: p.clone(),
1204                    },
1205                )
1206            })
1207            .collect::<BTreeMap<_, _>>();
1208
1209        for (nr, s) in constraints.for_each_restrictions(orientation) {
1210            values.insert(
1211                s.into(),
1212                llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
1213            );
1214        }
1215        llr_Expression::CodeBlock([store, llr_Expression::Struct { ty, values }].into())
1216    } else {
1217        layout_info
1218    }
1219}
1220
1221// Called for repeated components in a grid layout, to generate code to provide input for organize_grid_layout().
1222pub fn get_grid_layout_input_for_repeated(
1223    ctx: &mut ExpressionLoweringCtx,
1224    grid_cell: &GridLayoutCell,
1225) -> llr_Expression {
1226    let mut assignments = Vec::new();
1227
1228    fn convert_row_col_expr(expr: &RowColExpr, ctx: &ExpressionLoweringCtx) -> llr_Expression {
1229        match expr {
1230            RowColExpr::Literal(n) => llr_Expression::NumberLiteral((*n).into()),
1231            RowColExpr::Named(nr) => {
1232                llr_Expression::PropertyReference(ctx.map_property_reference(nr))
1233            }
1234            RowColExpr::Auto => llr_Expression::NumberLiteral(i_slint_common::ROW_COL_AUTO as _),
1235        }
1236    }
1237
1238    // Generate assignments to the `result` slice parameter: result[i] = struct { ... }
1239    let mut push_assignment =
1240        |i: usize, new_row_expr: &llr_Expression, grid_cell: &GridLayoutCell| {
1241            let row = convert_row_col_expr(&grid_cell.row_expr, &*ctx);
1242            let col = convert_row_col_expr(&grid_cell.col_expr, &*ctx);
1243            let rowspan = convert_row_col_expr(&grid_cell.rowspan_expr, &*ctx);
1244            let colspan = convert_row_col_expr(&grid_cell.colspan_expr, &*ctx);
1245            let value = make_struct(
1246                BuiltinPrivateStruct::GridLayoutInputData,
1247                [
1248                    ("new_row", Type::Bool, new_row_expr.clone()),
1249                    ("row", Type::Float32, row),
1250                    ("col", Type::Float32, col),
1251                    ("rowspan", Type::Float32, rowspan),
1252                    ("colspan", Type::Float32, colspan),
1253                ],
1254            );
1255            assignments.push(llr_Expression::SliceIndexAssignment {
1256                slice_name: SmolStr::new_static("result"),
1257                index: i,
1258                value: value.into(),
1259            });
1260        };
1261
1262    if let Some(child_items) = grid_cell.child_items.as_ref() {
1263        // Repeated Row: only handle static children here;
1264        // inner repeater children are handled by the code generators at runtime
1265        let mut new_row_expr = llr_Expression::BoolLiteral(true);
1266        let mut i = 0;
1267        for child_item in child_items.iter() {
1268            match child_item {
1269                crate::layout::RowChildTemplate::Static(layout_item) => {
1270                    let child_element = layout_item.element.borrow();
1271                    let child_cell = child_element.grid_layout_cell.as_ref().unwrap().borrow();
1272                    push_assignment(i, &new_row_expr, &child_cell);
1273                    new_row_expr = llr_Expression::BoolLiteral(false);
1274                    i += 1;
1275                }
1276                crate::layout::RowChildTemplate::Repeated { .. } => {
1277                    // Inner repeater children are filled at runtime by the code generators
1278                }
1279            }
1280        }
1281    } else {
1282        // Single repeated item
1283        // grid_cell.new_row is the static information from the slint file.
1284        // In practice, for repeated items within a row, whether we should start a new row
1285        // is more dynamic (e.g. if the previous item was in "if false"),
1286        // and tracked by a local variable "new_row" in the generated code.
1287        let new_row_expr = llr_Expression::ReadLocalVariable {
1288            name: SmolStr::new_static("new_row"),
1289            ty: Type::Bool,
1290        };
1291        push_assignment(0, &new_row_expr, grid_cell);
1292    }
1293
1294    llr_Expression::CodeBlock(assignments)
1295}
1296
1297/// Returns the row child template list for a repeated Row element.
1298///
1299/// Reads it from the already-lowered Row sub-component (which must have been
1300/// lowered before the parent's expression lowering — see the ordering in
1301/// `lower_sub_component`).
1302///
1303/// Returns `None` if this is a column-repeater (not a Row sub-component).
1304/// Returns `Some(vec)` with one entry per child in declaration order.
1305fn get_row_child_templates(
1306    outer_element: &ElementRc,
1307    ctx: &ExpressionLoweringCtx,
1308) -> Option<Vec<super::RowChildTemplateInfo>> {
1309    let comp = outer_element.borrow().base_type.as_component().clone();
1310    ctx.state.row_child_templates(&comp)
1311}
1312
1313/// Generate an expression that builds a FlexboxLayoutItemInfo for a repeated element
1314/// in a FlexboxLayout, reading flex properties from the component instance.
1315pub fn get_flexbox_layout_item_info_for_repeated(
1316    ctx: &mut ExpressionLoweringCtx,
1317    element: &ElementRc,
1318) -> llr_Expression {
1319    let prop_ref = |name: &'static str| -> Option<llr_Expression> {
1320        crate::layout::binding_reference(element, name)
1321            .map(|nr| llr_Expression::PropertyReference(ctx.map_property_reference(&nr)))
1322    };
1323
1324    let (align_self_ty, align_self_default) = default_align_self();
1325
1326    let grow = prop_ref("flex-grow").unwrap_or(llr_Expression::NumberLiteral(0.0));
1327    let shrink = prop_ref("flex-shrink").unwrap_or(llr_Expression::NumberLiteral(1.0));
1328    let basis = prop_ref("flex-basis").unwrap_or(llr_Expression::NumberLiteral(-1.0));
1329    let align_self = prop_ref("flex-align-self").unwrap_or(align_self_default);
1330    let order = prop_ref("flex-order").unwrap_or(llr_Expression::NumberLiteral(0.0));
1331
1332    make_struct(
1333        BuiltinPrivateStruct::FlexboxLayoutItemInfo,
1334        [
1335            (
1336                "constraint",
1337                crate::typeregister::layout_info_type().into(),
1338                llr_Expression::default_value_for_type(
1339                    &crate::typeregister::layout_info_type().into(),
1340                )
1341                .unwrap(),
1342            ),
1343            ("flex-grow", Type::Float32, grow),
1344            ("flex-shrink", Type::Float32, shrink),
1345            ("flex-basis", Type::Float32, basis),
1346            ("flex-align-self", align_self_ty, align_self),
1347            ("flex-order", Type::Int32, order),
1348        ],
1349    )
1350}