Skip to main content

i_slint_compiler/llr/
lower_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::cell::RefCell;
5use std::collections::{BTreeMap, HashMap};
6use std::rc::{Rc, Weak};
7
8use itertools::Either;
9use smol_str::{SmolStr, format_smolstr};
10
11use super::lower_to_item_tree::{LoweredElement, LoweredSubComponentMapping, LoweringState};
12use super::{
13    Animation, GridLayoutRepeatedElement, LayoutRepeatedElement, LocalMemberReference,
14    MemberReference, PropertyIdx,
15};
16use crate::expression_tree::{BuiltinFunction, Callable, Expression as tree_Expression};
17use crate::langtype::{BuiltinPrivateStruct, EnumerationValue, Struct, StructName, Type};
18use crate::layout::{GridLayoutCell, Orientation, RowColExpr};
19use crate::llr::ArrayOutput as llr_ArrayOutput;
20use crate::llr::Expression as llr_Expression;
21use crate::namedreference::NamedReference;
22use crate::object_tree::{Element, ElementRc, PropertyAnimation};
23use crate::typeregister::BUILTIN;
24
25pub struct ExpressionLoweringCtxInner<'a> {
26    pub component: &'a Rc<crate::object_tree::Component>,
27    /// The mapping for the current component
28    pub mapping: &'a LoweredSubComponentMapping,
29    pub parent: Option<&'a ExpressionLoweringCtxInner<'a>>,
30}
31#[derive(derive_more::Deref)]
32pub struct ExpressionLoweringCtx<'a> {
33    pub state: &'a mut LoweringState,
34    #[deref]
35    pub inner: ExpressionLoweringCtxInner<'a>,
36}
37
38impl ExpressionLoweringCtx<'_> {
39    pub fn map_property_reference(&self, from: &NamedReference) -> MemberReference {
40        let element = from.element();
41        let enclosing = &element.borrow().enclosing_component.upgrade().unwrap();
42        let mut level = 0;
43        let mut map = &self.inner;
44        if !enclosing.is_global() {
45            while !Rc::ptr_eq(enclosing, map.component) {
46                map = map.parent.unwrap_or_else(|| {
47                    panic!(
48                        "Could not find component for property reference {from:?} in component {:?}. Started with enclosing={:?}",
49                        self.component.id,
50                        enclosing.id
51                    )
52                });
53                level += 1;
54            }
55        }
56        let mut r = map.mapping.map_property_reference(from, self.state);
57        if let MemberReference::Relative { parent_level, .. } = &mut r {
58            *parent_level += level;
59        }
60        r
61    }
62}
63
64impl super::TypeResolutionContext for ExpressionLoweringCtx<'_> {
65    fn property_ty(&self, _: &MemberReference) -> &Type {
66        unimplemented!()
67    }
68}
69
70pub fn lower_expression(
71    expression: &tree_Expression,
72    ctx: &mut ExpressionLoweringCtx<'_>,
73) -> llr_Expression {
74    match expression {
75        tree_Expression::Invalid => {
76            panic!("internal error, encountered invalid expression at code generation time")
77        }
78        tree_Expression::Uncompiled(_) => panic!(),
79        tree_Expression::StringLiteral(s) => llr_Expression::StringLiteral(s.clone()),
80        tree_Expression::NumberLiteral(n, unit) => {
81            llr_Expression::NumberLiteral(unit.normalize(*n))
82        }
83        tree_Expression::BoolLiteral(b) => llr_Expression::BoolLiteral(*b),
84        tree_Expression::PropertyReference(nr) => {
85            llr_Expression::PropertyReference(ctx.map_property_reference(nr))
86        }
87        tree_Expression::ElementReference(e) => {
88            let elem = e.upgrade().unwrap();
89            let enclosing = elem.borrow().enclosing_component.upgrade().unwrap();
90            // When within a ShowPopupMenu builtin function, this is a reference to the root of the menu item tree
91            if Rc::ptr_eq(&elem, &enclosing.root_element)
92                && let Some(idx) = ctx
93                    .component
94                    .menu_item_tree
95                    .borrow()
96                    .iter()
97                    .position(|c| Rc::ptr_eq(c, &enclosing))
98            {
99                return llr_Expression::NumberLiteral(idx as _);
100            }
101
102            // We map an element reference to a reference to the property "" inside that native item
103            llr_Expression::PropertyReference(
104                ctx.map_property_reference(&NamedReference::new(&elem, SmolStr::default())),
105            )
106        }
107        tree_Expression::RepeaterIndexReference { element } => llr_Expression::PropertyReference(
108            repeater_special_property(element, ctx.component, PropertyIdx::REPEATER_INDEX),
109        ),
110        tree_Expression::RepeaterModelReference { element } => llr_Expression::PropertyReference(
111            repeater_special_property(element, ctx.component, PropertyIdx::REPEATER_DATA),
112        ),
113        tree_Expression::FunctionParameterReference { index, .. } => {
114            llr_Expression::FunctionParameterReference { index: *index }
115        }
116        tree_Expression::StoreLocalVariable { name, value } => llr_Expression::StoreLocalVariable {
117            name: name.clone(),
118            value: Box::new(lower_expression(value, ctx)),
119        },
120        tree_Expression::ReadLocalVariable { name, ty } => {
121            llr_Expression::ReadLocalVariable { name: name.clone(), ty: ty.clone() }
122        }
123        tree_Expression::StructFieldAccess { base, name } => llr_Expression::StructFieldAccess {
124            base: Box::new(lower_expression(base, ctx)),
125            name: name.clone(),
126        },
127        tree_Expression::ArrayIndex { array, index } => llr_Expression::ArrayIndex {
128            array: Box::new(lower_expression(array, ctx)),
129            index: Box::new(lower_expression(index, ctx)),
130        },
131        tree_Expression::Cast { from, to } => {
132            llr_Expression::Cast { from: Box::new(lower_expression(from, ctx)), to: to.clone() }
133        }
134        tree_Expression::CodeBlock(expr) => {
135            llr_Expression::CodeBlock(expr.iter().map(|e| lower_expression(e, ctx)).collect::<_>())
136        }
137        tree_Expression::FunctionCall { function, arguments, .. } => match function {
138            Callable::Builtin(BuiltinFunction::RestartTimer) => lower_restart_timer(arguments),
139            Callable::Builtin(BuiltinFunction::ShowPopupWindow) => {
140                lower_show_popup_window(arguments, ctx)
141            }
142            Callable::Builtin(BuiltinFunction::ClosePopupWindow) => {
143                lower_close_popup_window(arguments, ctx)
144            }
145            Callable::Builtin(f) => {
146                let mut arguments =
147                    arguments.iter().map(|e| lower_expression(e, ctx)).collect::<Vec<_>>();
148                // https://github.com/rust-lang/rust-clippy/issues/16191
149                #[allow(clippy::collapsible_if)]
150                if *f == BuiltinFunction::Translate {
151                    if let llr_Expression::Array { output, .. } = &mut arguments[3] {
152                        *output = llr_ArrayOutput::Slice;
153                    }
154                    #[cfg(feature = "bundle-translations")]
155                    if let Some(translation_builder) = ctx.state.translation_builder.as_mut() {
156                        return translation_builder.lower_translate_call(arguments);
157                    }
158                }
159                llr_Expression::BuiltinFunctionCall { function: f.clone(), arguments }
160            }
161            Callable::Callback(nr) => {
162                let arguments = arguments.iter().map(|e| lower_expression(e, ctx)).collect::<_>();
163                llr_Expression::CallBackCall { callback: ctx.map_property_reference(nr), arguments }
164            }
165            Callable::Function(nr)
166                if nr
167                    .element()
168                    .borrow()
169                    .native_class()
170                    .is_some_and(|n| n.properties.contains_key(nr.name())) =>
171            {
172                llr_Expression::ItemMemberFunctionCall { function: ctx.map_property_reference(nr) }
173            }
174            Callable::Function(nr) => {
175                let arguments = arguments.iter().map(|e| lower_expression(e, ctx)).collect::<_>();
176                llr_Expression::FunctionCall { function: ctx.map_property_reference(nr), arguments }
177            }
178        },
179        tree_Expression::SelfAssignment { lhs, rhs, op, .. } => {
180            lower_assignment(lhs, rhs, *op, ctx)
181        }
182        tree_Expression::BinaryExpression { lhs, rhs, op } => llr_Expression::BinaryExpression {
183            lhs: Box::new(lower_expression(lhs, ctx)),
184            rhs: Box::new(lower_expression(rhs, ctx)),
185            op: *op,
186        },
187        tree_Expression::UnaryOp { sub, op } => {
188            llr_Expression::UnaryOp { sub: Box::new(lower_expression(sub, ctx)), op: *op }
189        }
190        tree_Expression::ImageReference { resource_ref, nine_slice, .. } => {
191            llr_Expression::ImageReference {
192                resource_ref: resource_ref.clone(),
193                nine_slice: *nine_slice,
194            }
195        }
196        tree_Expression::Condition { condition, true_expr, false_expr } => {
197            let (true_ty, false_ty) = (true_expr.ty(), false_expr.ty());
198            llr_Expression::Condition {
199                condition: Box::new(lower_expression(condition, ctx)),
200                true_expr: Box::new(lower_expression(true_expr, ctx)),
201                false_expr: if false_ty == Type::Invalid
202                    || false_ty == Type::Void
203                    || true_ty == false_ty
204                {
205                    Box::new(lower_expression(false_expr, ctx))
206                } else {
207                    // Because the type of the Condition is based on the false expression, we need to insert a cast
208                    Box::new(llr_Expression::Cast {
209                        from: Box::new(lower_expression(false_expr, ctx)),
210                        to: Type::Void,
211                    })
212                },
213            }
214        }
215        tree_Expression::Array { element_ty, values } => llr_Expression::Array {
216            element_ty: element_ty.clone(),
217            values: values.iter().map(|e| lower_expression(e, ctx)).collect::<_>(),
218            output: llr_ArrayOutput::Model,
219        },
220        tree_Expression::Struct { ty, values } => llr_Expression::Struct {
221            ty: ty.clone(),
222            values: values
223                .iter()
224                .map(|(s, e)| (s.clone(), lower_expression(e, ctx)))
225                .collect::<_>(),
226        },
227        tree_Expression::PathData(data) => compile_path(data, ctx),
228        tree_Expression::EasingCurve(x) => llr_Expression::EasingCurve(x.clone()),
229        tree_Expression::LinearGradient { angle, stops } => llr_Expression::LinearGradient {
230            angle: Box::new(lower_expression(angle, ctx)),
231            stops: stops
232                .iter()
233                .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
234                .collect::<_>(),
235        },
236        tree_Expression::RadialGradient { stops } => llr_Expression::RadialGradient {
237            stops: stops
238                .iter()
239                .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
240                .collect::<_>(),
241        },
242        tree_Expression::ConicGradient { from_angle, stops } => llr_Expression::ConicGradient {
243            from_angle: Box::new(lower_expression(from_angle, ctx)),
244            stops: stops
245                .iter()
246                .map(|(a, b)| (lower_expression(a, ctx), lower_expression(b, ctx)))
247                .collect::<_>(),
248        },
249        tree_Expression::EnumerationValue(e) => llr_Expression::EnumerationValue(e.clone()),
250        tree_Expression::ReturnStatement(..) => {
251            panic!("The remove return pass should have removed all return")
252        }
253        tree_Expression::LayoutCacheAccess {
254            layout_cache_prop,
255            index,
256            repeater_index,
257            entries_per_item,
258        } => llr_Expression::LayoutCacheAccess {
259            layout_cache_prop: ctx.map_property_reference(layout_cache_prop),
260            index: *index,
261            repeater_index: repeater_index.as_ref().map(|e| lower_expression(e, ctx).into()),
262            entries_per_item: *entries_per_item,
263        },
264        tree_Expression::OrganizeGridLayout(l) => organize_grid_layout(l, ctx),
265        tree_Expression::ComputeLayoutInfo(l, o) => compute_layout_info(l, *o, ctx),
266        tree_Expression::ComputeGridLayoutInfo {
267            layout_organized_data_prop,
268            layout,
269            orientation,
270        } => compute_grid_layout_info(layout_organized_data_prop, layout, *orientation, ctx),
271        tree_Expression::SolveLayout(l, o) => solve_layout(l, *o, ctx),
272        tree_Expression::SolveGridLayout { layout_organized_data_prop, layout, orientation } => {
273            solve_grid_layout(layout_organized_data_prop, layout, *orientation, ctx)
274        }
275        tree_Expression::MinMax { ty, op, lhs, rhs } => llr_Expression::MinMax {
276            ty: ty.clone(),
277            op: *op,
278            lhs: Box::new(lower_expression(lhs, ctx)),
279            rhs: Box::new(lower_expression(rhs, ctx)),
280        },
281        tree_Expression::EmptyComponentFactory => llr_Expression::EmptyComponentFactory,
282        tree_Expression::DebugHook { expression, .. } => lower_expression(expression, ctx),
283    }
284}
285
286fn lower_assignment(
287    lhs: &tree_Expression,
288    rhs: &tree_Expression,
289    op: char,
290    ctx: &mut ExpressionLoweringCtx,
291) -> llr_Expression {
292    match lhs {
293        tree_Expression::PropertyReference(nr) => {
294            let rhs = lower_expression(rhs, ctx);
295            let property = ctx.map_property_reference(nr);
296            let value = if op == '=' {
297                rhs
298            } else {
299                llr_Expression::BinaryExpression {
300                    lhs: llr_Expression::PropertyReference(property.clone()).into(),
301                    rhs: rhs.into(),
302                    op,
303                }
304            }
305            .into();
306            llr_Expression::PropertyAssignment { property, value }
307        }
308        tree_Expression::StructFieldAccess { base, name } => {
309            let ty = base.ty();
310
311            static COUNT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
312            let unique_name = format_smolstr!(
313                "struct_assignment{}",
314                COUNT.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
315            );
316            let s = tree_Expression::StoreLocalVariable {
317                name: unique_name.clone(),
318                value: base.clone(),
319            };
320            let lower_base =
321                tree_Expression::ReadLocalVariable { name: unique_name, ty: ty.clone() };
322            let mut values = HashMap::new();
323            let Type::Struct(ty) = ty else { unreachable!() };
324
325            for field in ty.fields.keys() {
326                let e = if field != name {
327                    tree_Expression::StructFieldAccess {
328                        base: lower_base.clone().into(),
329                        name: field.clone(),
330                    }
331                } else if op == '=' {
332                    rhs.clone()
333                } else {
334                    tree_Expression::BinaryExpression {
335                        lhs: tree_Expression::StructFieldAccess {
336                            base: lower_base.clone().into(),
337                            name: field.clone(),
338                        }
339                        .into(),
340                        rhs: Box::new(rhs.clone()),
341                        op,
342                    }
343                };
344                values.insert(field.clone(), e);
345            }
346
347            let new_value =
348                tree_Expression::CodeBlock(vec![s, tree_Expression::Struct { ty, values }]);
349            lower_assignment(base, &new_value, '=', ctx)
350        }
351        tree_Expression::RepeaterModelReference { element } => {
352            let rhs = lower_expression(rhs, ctx);
353            let prop =
354                repeater_special_property(element, ctx.component, PropertyIdx::REPEATER_DATA);
355
356            let level = match &prop {
357                MemberReference::Relative { parent_level, .. } => *parent_level,
358                _ => 0,
359            };
360
361            let value = Box::new(if op == '=' {
362                rhs
363            } else {
364                llr_Expression::BinaryExpression {
365                    lhs: llr_Expression::PropertyReference(prop).into(),
366                    rhs: rhs.into(),
367                    op,
368                }
369            });
370
371            llr_Expression::ModelDataAssignment { level, value }
372        }
373        tree_Expression::ArrayIndex { array, index } => {
374            let rhs = lower_expression(rhs, ctx);
375            let array = Box::new(lower_expression(array, ctx));
376            let index = Box::new(lower_expression(index, ctx));
377            let value = Box::new(if op == '=' {
378                rhs
379            } else {
380                // FIXME: this will compute the index and the array twice:
381                // Ideally we should store the index and the array in local variable
382                llr_Expression::BinaryExpression {
383                    lhs: llr_Expression::ArrayIndex { array: array.clone(), index: index.clone() }
384                        .into(),
385                    rhs: rhs.into(),
386                    op,
387                }
388            });
389
390            llr_Expression::ArrayIndexAssignment { array, index, value }
391        }
392        _ => panic!("not a rvalue"),
393    }
394}
395
396pub fn repeater_special_property(
397    element: &Weak<RefCell<Element>>,
398    component: &Rc<crate::object_tree::Component>,
399    property_index: PropertyIdx,
400) -> MemberReference {
401    let enclosing = element.upgrade().unwrap().borrow().enclosing_component.upgrade().unwrap();
402    let mut parent_level = 0;
403    let mut component = component.clone();
404    while !Rc::ptr_eq(&enclosing, &component) {
405        component = component
406            .parent_element
407            .upgrade()
408            .unwrap()
409            .borrow()
410            .enclosing_component
411            .upgrade()
412            .unwrap();
413        parent_level += 1;
414    }
415    MemberReference::Relative {
416        parent_level: parent_level - 1,
417        local_reference: LocalMemberReference {
418            sub_component_path: Vec::new(),
419            reference: property_index.into(),
420        },
421    }
422}
423
424fn lower_restart_timer(args: &[tree_Expression]) -> llr_Expression {
425    if let [tree_Expression::ElementReference(e)] = args {
426        let timer_element = e.upgrade().unwrap();
427        let timer_comp = timer_element.borrow().enclosing_component.upgrade().unwrap();
428
429        let timer_list = timer_comp.timers.borrow();
430        let timer_index = timer_list
431            .iter()
432            .position(|t| Rc::ptr_eq(&t.element.upgrade().unwrap(), &timer_element))
433            .unwrap();
434
435        llr_Expression::BuiltinFunctionCall {
436            function: BuiltinFunction::RestartTimer,
437            arguments: vec![llr_Expression::NumberLiteral(timer_index as _)],
438        }
439    } else {
440        panic!("invalid arguments to RestartTimer");
441    }
442}
443
444fn lower_show_popup_window(
445    args: &[tree_Expression],
446    ctx: &mut ExpressionLoweringCtx,
447) -> llr_Expression {
448    if let [tree_Expression::ElementReference(e)] = args {
449        let popup_window = e.upgrade().unwrap();
450        let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
451        let parent_component = pop_comp
452            .parent_element
453            .upgrade()
454            .unwrap()
455            .borrow()
456            .enclosing_component
457            .upgrade()
458            .unwrap();
459        let popup_list = parent_component.popup_windows.borrow();
460        let (popup_index, popup) = popup_list
461            .iter()
462            .enumerate()
463            .find(|(_, p)| Rc::ptr_eq(&p.component, &pop_comp))
464            .unwrap();
465        let item_ref = lower_expression(
466            &tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
467            ctx,
468        );
469
470        llr_Expression::BuiltinFunctionCall {
471            function: BuiltinFunction::ShowPopupWindow,
472            arguments: vec![
473                llr_Expression::NumberLiteral(popup_index as _),
474                llr_Expression::EnumerationValue(popup.close_policy.clone()),
475                item_ref,
476            ],
477        }
478    } else {
479        panic!("invalid arguments to ShowPopupWindow");
480    }
481}
482
483fn lower_close_popup_window(
484    args: &[tree_Expression],
485    ctx: &mut ExpressionLoweringCtx,
486) -> llr_Expression {
487    if let [tree_Expression::ElementReference(e)] = args {
488        let popup_window = e.upgrade().unwrap();
489        let pop_comp = popup_window.borrow().enclosing_component.upgrade().unwrap();
490        let parent_component = pop_comp
491            .parent_element
492            .upgrade()
493            .unwrap()
494            .borrow()
495            .enclosing_component
496            .upgrade()
497            .unwrap();
498        let popup_list = parent_component.popup_windows.borrow();
499        let (popup_index, popup) = popup_list
500            .iter()
501            .enumerate()
502            .find(|(_, p)| Rc::ptr_eq(&p.component, &pop_comp))
503            .unwrap();
504        let item_ref = lower_expression(
505            &tree_Expression::ElementReference(Rc::downgrade(&popup.parent_element)),
506            ctx,
507        );
508
509        llr_Expression::BuiltinFunctionCall {
510            function: BuiltinFunction::ClosePopupWindow,
511            arguments: vec![llr_Expression::NumberLiteral(popup_index as _), item_ref],
512        }
513    } else {
514        panic!("invalid arguments to ShowPopupWindow");
515    }
516}
517
518pub fn lower_animation(a: &PropertyAnimation, ctx: &mut ExpressionLoweringCtx<'_>) -> Animation {
519    fn lower_animation_element(
520        a: &ElementRc,
521        ctx: &mut ExpressionLoweringCtx<'_>,
522    ) -> llr_Expression {
523        llr_Expression::Struct {
524            values: animation_fields()
525                .map(|(k, ty)| {
526                    let e = a.borrow().bindings.get(&k).map_or_else(
527                        || llr_Expression::default_value_for_type(&ty).unwrap(),
528                        |v| lower_expression(&v.borrow().expression, ctx),
529                    );
530                    (k, e)
531                })
532                .collect::<_>(),
533            ty: animation_ty(),
534        }
535    }
536
537    fn animation_fields() -> impl Iterator<Item = (SmolStr, Type)> {
538        IntoIterator::into_iter([
539            (SmolStr::new_static("duration"), Type::Int32),
540            (SmolStr::new_static("iteration-count"), Type::Float32),
541            (
542                SmolStr::new_static("direction"),
543                Type::Enumeration(BUILTIN.with(|e| e.enums.AnimationDirection.clone())),
544            ),
545            (SmolStr::new_static("easing"), Type::Easing),
546            (SmolStr::new_static("delay"), Type::Int32),
547        ])
548    }
549
550    fn animation_ty() -> Rc<Struct> {
551        Rc::new(Struct {
552            fields: animation_fields().collect(),
553            name: BuiltinPrivateStruct::PropertyAnimation.into(),
554        })
555    }
556
557    match a {
558        PropertyAnimation::Static(a) => Animation::Static(lower_animation_element(a, ctx)),
559        PropertyAnimation::Transition { state_ref, animations } => {
560            let set_state = llr_Expression::StoreLocalVariable {
561                name: "state".into(),
562                value: Box::new(lower_expression(state_ref, ctx)),
563            };
564            let animation_ty = Type::Struct(animation_ty());
565            let mut get_anim = llr_Expression::default_value_for_type(&animation_ty).unwrap();
566            for tr in animations.iter().rev() {
567                let condition = lower_expression(
568                    &tr.condition(tree_Expression::ReadLocalVariable {
569                        name: "state".into(),
570                        ty: state_ref.ty(),
571                    }),
572                    ctx,
573                );
574                get_anim = llr_Expression::Condition {
575                    condition: Box::new(condition),
576                    true_expr: Box::new(lower_animation_element(&tr.animation, ctx)),
577                    false_expr: Box::new(get_anim),
578                }
579            }
580            let result = llr_Expression::Struct {
581                // This is going to be a tuple
582                ty: Rc::new(Struct {
583                    fields: IntoIterator::into_iter([
584                        (SmolStr::new_static("0"), animation_ty),
585                        // The type is an instant, which does not exist in our type system
586                        (SmolStr::new_static("1"), Type::Invalid),
587                    ])
588                    .collect(),
589                    name: StructName::None,
590                }),
591                values: IntoIterator::into_iter([
592                    (SmolStr::new_static("0"), get_anim),
593                    (
594                        SmolStr::new_static("1"),
595                        llr_Expression::StructFieldAccess {
596                            base: llr_Expression::ReadLocalVariable {
597                                name: "state".into(),
598                                ty: state_ref.ty(),
599                            }
600                            .into(),
601                            name: "change_time".into(),
602                        },
603                    ),
604                ])
605                .collect(),
606            };
607            Animation::Transition(llr_Expression::CodeBlock(vec![set_state, result]))
608        }
609    }
610}
611
612fn empty_int32_slice() -> llr_Expression {
613    llr_Expression::Array {
614        element_ty: Type::Int32,
615        values: Vec::new(),
616        output: llr_ArrayOutput::Slice,
617    }
618}
619
620fn compute_grid_layout_info(
621    layout_organized_data_prop: &NamedReference,
622    layout: &crate::layout::GridLayout,
623    o: Orientation,
624    ctx: &mut ExpressionLoweringCtx,
625) -> llr_Expression {
626    let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
627    let organized_cells = ctx.map_property_reference(layout_organized_data_prop);
628    let constraints_result = grid_layout_cell_constraints(layout, o, ctx);
629    let orientation_literal = llr_Expression::EnumerationValue(EnumerationValue {
630        value: o as _,
631        enumeration: crate::typeregister::BUILTIN.with(|b| b.enums.Orientation.clone()),
632    });
633
634    let sub_expression = llr_Expression::ExtraBuiltinFunctionCall {
635        function: "grid_layout_info".into(),
636        arguments: vec![
637            llr_Expression::PropertyReference(organized_cells),
638            constraints_result.cells,
639            if constraints_result.compute_cells.is_none() {
640                empty_int32_slice()
641            } else {
642                llr_Expression::ReadLocalVariable {
643                    name: "repeated_indices".into(),
644                    ty: Type::Array(Type::Int32.into()),
645                }
646            },
647            if constraints_result.compute_cells.is_none() {
648                empty_int32_slice()
649            } else {
650                llr_Expression::ReadLocalVariable {
651                    name: "repeater_steps".into(),
652                    ty: Type::Array(Type::Int32.into()),
653                }
654            },
655            spacing,
656            padding,
657            orientation_literal,
658        ],
659        return_ty: crate::typeregister::layout_info_type().into(),
660    };
661    match constraints_result.compute_cells {
662        Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
663            cells_variable,
664            repeater_indices_var_name: Some("repeated_indices".into()),
665            repeater_steps_var_name: Some("repeater_steps".into()),
666            elements,
667            orientation: o,
668            sub_expression: Box::new(sub_expression),
669        },
670        None => sub_expression,
671    }
672}
673
674fn compute_layout_info(
675    l: &crate::layout::Layout,
676    o: Orientation,
677    ctx: &mut ExpressionLoweringCtx,
678) -> llr_Expression {
679    match l {
680        crate::layout::Layout::GridLayout(_) => {
681            panic!("compute_layout_info called on GridLayout, use compute_grid_layout_info");
682        }
683        crate::layout::Layout::BoxLayout(layout) => {
684            let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
685            let bld = box_layout_data(layout, o, ctx);
686            let sub_expression = if o == layout.orientation {
687                llr_Expression::ExtraBuiltinFunctionCall {
688                    function: "box_layout_info".into(),
689                    arguments: vec![bld.cells, spacing, padding, bld.alignment],
690                    return_ty: crate::typeregister::layout_info_type().into(),
691                }
692            } else {
693                llr_Expression::ExtraBuiltinFunctionCall {
694                    function: "box_layout_info_ortho".into(),
695                    arguments: vec![bld.cells, padding],
696                    return_ty: crate::typeregister::layout_info_type().into(),
697                }
698            };
699            match bld.compute_cells {
700                Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
701                    cells_variable,
702                    repeater_indices_var_name: None,
703                    repeater_steps_var_name: None,
704                    elements,
705                    orientation: o,
706                    sub_expression: Box::new(sub_expression),
707                },
708                None => sub_expression,
709            }
710        }
711    }
712}
713
714fn organize_grid_layout(
715    layout: &crate::layout::GridLayout,
716    ctx: &mut ExpressionLoweringCtx,
717) -> llr_Expression {
718    let input_data = grid_layout_input_data(layout, ctx);
719
720    if let Some(button_roles) = &layout.dialog_button_roles {
721        let e = crate::typeregister::BUILTIN.with(|e| e.enums.DialogButtonRole.clone());
722        let roles = button_roles
723            .iter()
724            .map(|r| {
725                llr_Expression::EnumerationValue(EnumerationValue {
726                    value: e.values.iter().position(|x| x == r).unwrap() as _,
727                    enumeration: e.clone(),
728                })
729            })
730            .collect();
731        let roles_expr = llr_Expression::Array {
732            element_ty: Type::Enumeration(e),
733            values: roles,
734            output: llr_ArrayOutput::Slice,
735        };
736        llr_Expression::ExtraBuiltinFunctionCall {
737            function: "organize_dialog_button_layout".into(),
738            arguments: vec![input_data.cells, roles_expr],
739            return_ty: Type::Array(Type::Int32.into()),
740        }
741    } else {
742        let sub_expression = llr_Expression::ExtraBuiltinFunctionCall {
743            function: "organize_grid_layout".into(),
744            arguments: vec![
745                input_data.cells,
746                if input_data.compute_cells.is_none() {
747                    empty_int32_slice()
748                } else {
749                    llr_Expression::ReadLocalVariable {
750                        name: SmolStr::new_static("repeated_indices"),
751                        ty: Type::Array(Type::Int32.into()),
752                    }
753                },
754                if input_data.compute_cells.is_none() {
755                    empty_int32_slice()
756                } else {
757                    llr_Expression::ReadLocalVariable {
758                        name: SmolStr::new_static("repeater_steps"),
759                        ty: Type::Array(Type::Int32.into()),
760                    }
761                },
762            ],
763            return_ty: Type::Array(Type::Int32.into()),
764        };
765        if let Some((cells_variable, elements)) = input_data.compute_cells {
766            llr_Expression::WithGridInputData {
767                cells_variable,
768                repeater_indices_var_name: SmolStr::new_static("repeated_indices"),
769                repeater_steps_var_name: SmolStr::new_static("repeater_steps"),
770                elements,
771                sub_expression: Box::new(sub_expression),
772            }
773        } else {
774            sub_expression
775        }
776    }
777}
778
779fn solve_grid_layout(
780    layout_organized_data_prop: &NamedReference,
781    layout: &crate::layout::GridLayout,
782    o: Orientation,
783    ctx: &mut ExpressionLoweringCtx,
784) -> llr_Expression {
785    let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
786    let cells = ctx.map_property_reference(layout_organized_data_prop);
787    let size = layout_geometry_size(&layout.geometry.rect, o, ctx);
788    let orientation_expr = llr_Expression::EnumerationValue(EnumerationValue {
789        value: o as _,
790        enumeration: crate::typeregister::BUILTIN.with(|b| b.enums.Orientation.clone()),
791    });
792    let data = make_struct(
793        BuiltinPrivateStruct::GridLayoutData,
794        [
795            ("size", Type::Float32, size),
796            ("spacing", Type::Float32, spacing),
797            ("padding", padding.ty(ctx), padding),
798            ("organized_data", Type::ArrayOfU16, llr_Expression::PropertyReference(cells)),
799        ],
800    );
801    let constraints_result = grid_layout_cell_constraints(layout, o, ctx);
802
803    match constraints_result.compute_cells {
804        Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
805            cells_variable: cells_variable.clone(),
806            repeater_indices_var_name: Some("repeated_indices".into()),
807            repeater_steps_var_name: Some("repeater_steps".into()),
808            elements,
809            orientation: o,
810            sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
811                function: "solve_grid_layout".into(),
812                arguments: vec![
813                    data,
814                    llr_Expression::ReadLocalVariable {
815                        name: cells_variable.into(),
816                        ty: constraints_result.cells.ty(ctx),
817                    },
818                    orientation_expr,
819                    llr_Expression::ReadLocalVariable {
820                        name: "repeated_indices".into(),
821                        ty: Type::Array(Type::Int32.into()),
822                    },
823                    llr_Expression::ReadLocalVariable {
824                        name: "repeater_steps".into(),
825                        ty: Type::Array(Type::Int32.into()),
826                    },
827                ],
828                return_ty: Type::LayoutCache,
829            }),
830        },
831        None => llr_Expression::ExtraBuiltinFunctionCall {
832            function: "solve_grid_layout".into(),
833            arguments: vec![
834                data,
835                constraints_result.cells,
836                orientation_expr,
837                empty_int32_slice(),
838                empty_int32_slice(),
839            ],
840            return_ty: Type::LayoutCache,
841        },
842    }
843}
844
845fn solve_layout(
846    l: &crate::layout::Layout,
847    o: Orientation,
848    ctx: &mut ExpressionLoweringCtx,
849) -> llr_Expression {
850    match l {
851        crate::layout::Layout::BoxLayout(layout) => {
852            let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
853            let bld = box_layout_data(layout, o, ctx);
854            let size = layout_geometry_size(&layout.geometry.rect, o, ctx);
855            let data = make_struct(
856                BuiltinPrivateStruct::BoxLayoutData,
857                [
858                    ("size", Type::Float32, size),
859                    ("spacing", Type::Float32, spacing),
860                    ("padding", padding.ty(ctx), padding),
861                    (
862                        "alignment",
863                        crate::typeregister::BUILTIN
864                            .with(|e| Type::Enumeration(e.enums.LayoutAlignment.clone())),
865                        bld.alignment,
866                    ),
867                    ("cells", bld.cells.ty(ctx), bld.cells),
868                ],
869            );
870            match bld.compute_cells {
871                Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
872                    cells_variable,
873                    repeater_indices_var_name: Some("repeated_indices".into()),
874                    repeater_steps_var_name: None,
875                    elements,
876                    orientation: o,
877                    sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
878                        function: "solve_box_layout".into(),
879                        arguments: vec![
880                            data,
881                            llr_Expression::ReadLocalVariable {
882                                name: "repeated_indices".into(),
883                                ty: Type::Array(Type::Int32.into()),
884                            },
885                        ],
886                        return_ty: Type::LayoutCache,
887                    }),
888                },
889                None => llr_Expression::ExtraBuiltinFunctionCall {
890                    function: "solve_box_layout".into(),
891                    arguments: vec![data, empty_int32_slice()],
892                    return_ty: Type::LayoutCache,
893                },
894            }
895        }
896        _ => panic!("solve_layout is only supported for BoxLayout"),
897    }
898}
899
900struct BoxLayoutDataResult {
901    alignment: llr_Expression,
902    cells: llr_Expression,
903    /// When there are repeater involved, we need to do a WithLayoutItemInfo with the
904    /// given cell variable and elements
905    compute_cells: Option<(String, Vec<Either<llr_Expression, LayoutRepeatedElement>>)>,
906}
907
908fn make_layout_cell_data_struct(layout_info: llr_Expression) -> llr_Expression {
909    make_struct(
910        BuiltinPrivateStruct::LayoutItemInfo,
911        [("constraint", crate::typeregister::layout_info_type().into(), layout_info)],
912    )
913}
914
915fn box_layout_data(
916    layout: &crate::layout::BoxLayout,
917    orientation: Orientation,
918    ctx: &mut ExpressionLoweringCtx,
919) -> BoxLayoutDataResult {
920    let alignment = if let Some(expr) = &layout.geometry.alignment {
921        llr_Expression::PropertyReference(ctx.map_property_reference(expr))
922    } else {
923        let e = crate::typeregister::BUILTIN.with(|e| e.enums.LayoutAlignment.clone());
924        llr_Expression::EnumerationValue(EnumerationValue {
925            value: e.default_value,
926            enumeration: e,
927        })
928    };
929
930    let repeater_count =
931        layout.elems.iter().filter(|i| i.element.borrow().repeated.is_some()).count();
932
933    let element_ty = crate::typeregister::box_layout_cell_data_type();
934
935    if repeater_count == 0 {
936        let cells = llr_Expression::Array {
937            values: layout
938                .elems
939                .iter()
940                .map(|li| {
941                    let layout_info =
942                        get_layout_info(&li.element, ctx, &li.constraints, orientation);
943                    make_layout_cell_data_struct(layout_info)
944                })
945                .collect(),
946            element_ty,
947            output: llr_ArrayOutput::Slice,
948        };
949        BoxLayoutDataResult { alignment, cells, compute_cells: None }
950    } else {
951        let mut elements = Vec::new();
952        for item in &layout.elems {
953            if item.element.borrow().repeated.is_some() {
954                let repeater_index =
955                    match ctx.mapping.element_mapping.get(&item.element.clone().into()).unwrap() {
956                        LoweredElement::Repeated { repeated_index } => *repeated_index,
957                        _ => panic!(),
958                    };
959                elements.push(Either::Right(LayoutRepeatedElement {
960                    repeater_index,
961                    repeated_children_count: None,
962                }))
963            } else {
964                let layout_info =
965                    get_layout_info(&item.element, ctx, &item.constraints, orientation);
966                elements.push(Either::Left(make_layout_cell_data_struct(layout_info)));
967            }
968        }
969        let cells = llr_Expression::ReadLocalVariable {
970            name: "cells".into(),
971            ty: Type::Array(Rc::new(crate::typeregister::layout_info_type().into())),
972        };
973        BoxLayoutDataResult { alignment, cells, compute_cells: Some(("cells".into(), elements)) }
974    }
975}
976
977struct GridLayoutCellConstraintsResult {
978    cells: llr_Expression,
979    /// When there are repeater involved, we need to do a WithLayoutItemInfo with the
980    /// given cell variable and elements
981    compute_cells: Option<(String, Vec<Either<llr_Expression, LayoutRepeatedElement>>)>,
982}
983
984fn grid_layout_cell_constraints(
985    layout: &crate::layout::GridLayout,
986    orientation: Orientation,
987    ctx: &mut ExpressionLoweringCtx,
988) -> GridLayoutCellConstraintsResult {
989    let repeater_count =
990        layout.elems.iter().filter(|i| i.item.element.borrow().repeated.is_some()).count();
991
992    let element_ty = crate::typeregister::box_layout_cell_data_type();
993
994    if repeater_count == 0 {
995        let cells = llr_Expression::Array {
996            element_ty,
997            values: layout
998                .elems
999                .iter()
1000                .map(|li| {
1001                    let layout_info =
1002                        get_layout_info(&li.item.element, ctx, &li.item.constraints, orientation);
1003                    make_layout_cell_data_struct(layout_info)
1004                })
1005                .collect(),
1006            output: llr_ArrayOutput::Slice,
1007        };
1008        GridLayoutCellConstraintsResult { cells, compute_cells: None }
1009    } else {
1010        let mut elements = Vec::new();
1011        for item in &layout.elems {
1012            if item.item.element.borrow().repeated.is_some() {
1013                let repeater_index = match ctx
1014                    .mapping
1015                    .element_mapping
1016                    .get(&item.item.element.clone().into())
1017                    .unwrap()
1018                {
1019                    LoweredElement::Repeated { repeated_index } => *repeated_index,
1020                    _ => panic!(),
1021                };
1022                let cell = item.cell.borrow();
1023                let repeated_children_count = cell.child_items.as_ref().map(|c| c.len());
1024                elements.push(Either::Right(LayoutRepeatedElement {
1025                    repeater_index,
1026                    repeated_children_count,
1027                }));
1028            } else {
1029                let layout_info =
1030                    get_layout_info(&item.item.element, ctx, &item.item.constraints, orientation);
1031                elements.push(Either::Left(make_layout_cell_data_struct(layout_info)));
1032            }
1033        }
1034        let cells = llr_Expression::ReadLocalVariable {
1035            name: "cells".into(),
1036            ty: Type::Array(Rc::new(crate::typeregister::layout_info_type().into())),
1037        };
1038        GridLayoutCellConstraintsResult { cells, compute_cells: Some(("cells".into(), elements)) }
1039    }
1040}
1041
1042struct GridLayoutInputDataResult {
1043    cells: llr_Expression,
1044    /// When there are repeaters involved, we need to do a WithGridInputData with the
1045    /// given cell variable and elements
1046    compute_cells: Option<(String, Vec<Either<llr_Expression, GridLayoutRepeatedElement>>)>,
1047}
1048
1049// helper for organize_grid_layout()
1050fn grid_layout_input_data(
1051    layout: &crate::layout::GridLayout,
1052    ctx: &mut ExpressionLoweringCtx,
1053) -> GridLayoutInputDataResult {
1054    let propref = |named_ref: &RowColExpr| match named_ref {
1055        RowColExpr::Literal(n) => llr_Expression::NumberLiteral((*n).into()),
1056        RowColExpr::Named(nr) => llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
1057        RowColExpr::Auto => llr_Expression::NumberLiteral(i_slint_common::ROW_COL_AUTO as _),
1058    };
1059    let input_data_for_cell = |elem: &crate::layout::GridLayoutElement,
1060                               new_row_expr: llr_Expression| {
1061        let row_expr = propref(&elem.cell.borrow().row_expr);
1062        let col_expr = propref(&elem.cell.borrow().col_expr);
1063        let rowspan_expr = propref(&elem.cell.borrow().rowspan_expr);
1064        let colspan_expr = propref(&elem.cell.borrow().colspan_expr);
1065
1066        make_struct(
1067            BuiltinPrivateStruct::GridLayoutInputData,
1068            [
1069                ("new_row", Type::Bool, new_row_expr),
1070                ("row", Type::Float32, row_expr),
1071                ("col", Type::Float32, col_expr),
1072                ("rowspan", Type::Float32, rowspan_expr),
1073                ("colspan", Type::Float32, colspan_expr),
1074            ],
1075        )
1076    };
1077    let repeater_count =
1078        layout.elems.iter().filter(|i| i.item.element.borrow().repeated.is_some()).count();
1079
1080    let element_ty = grid_layout_input_data_ty();
1081
1082    if repeater_count == 0 {
1083        let cells = llr_Expression::Array {
1084            element_ty,
1085            values: layout
1086                .elems
1087                .iter()
1088                .map(|elem| {
1089                    input_data_for_cell(
1090                        elem,
1091                        llr_Expression::BoolLiteral(elem.cell.borrow().new_row),
1092                    )
1093                })
1094                .collect(),
1095            output: llr_ArrayOutput::Slice,
1096        };
1097        GridLayoutInputDataResult { cells, compute_cells: None }
1098    } else {
1099        let mut elements = Vec::new();
1100        let mut after_repeater_in_same_row = false;
1101        for item in &layout.elems {
1102            let new_row = item.cell.borrow().new_row;
1103            if new_row {
1104                after_repeater_in_same_row = false;
1105            }
1106            if item.item.element.borrow().repeated.is_some() {
1107                let repeater_index = match ctx
1108                    .mapping
1109                    .element_mapping
1110                    .get(&item.item.element.clone().into())
1111                    .unwrap()
1112                {
1113                    LoweredElement::Repeated { repeated_index } => *repeated_index,
1114                    _ => panic!(),
1115                };
1116                let cell = item.cell.borrow();
1117                let repeated_children_count = cell.child_items.as_ref().map(|c| c.len());
1118                let repeated_element =
1119                    GridLayoutRepeatedElement { new_row, repeater_index, repeated_children_count };
1120                elements.push(Either::Right(repeated_element));
1121                after_repeater_in_same_row = true;
1122            } else {
1123                let new_row_expr = if new_row || !after_repeater_in_same_row {
1124                    llr_Expression::BoolLiteral(new_row)
1125                } else {
1126                    llr_Expression::ReadLocalVariable {
1127                        name: SmolStr::new_static("new_row"),
1128                        ty: Type::Bool,
1129                    }
1130                };
1131                elements.push(Either::Left(input_data_for_cell(item, new_row_expr)));
1132            }
1133        }
1134        let cells = llr_Expression::ReadLocalVariable {
1135            name: "cells".into(),
1136            ty: Type::Array(Rc::new(element_ty)),
1137        };
1138        GridLayoutInputDataResult { cells, compute_cells: Some(("cells".into(), elements)) }
1139    }
1140}
1141
1142pub(super) fn grid_layout_input_data_ty() -> Type {
1143    Type::Struct(Rc::new(Struct {
1144        fields: IntoIterator::into_iter([
1145            (SmolStr::new_static("new_row"), Type::Bool),
1146            (SmolStr::new_static("row"), Type::Int32),
1147            (SmolStr::new_static("col"), Type::Int32),
1148            (SmolStr::new_static("rowspan"), Type::Int32),
1149            (SmolStr::new_static("colspan"), Type::Int32),
1150        ])
1151        .collect(),
1152        name: BuiltinPrivateStruct::GridLayoutInputData.into(),
1153    }))
1154}
1155
1156fn generate_layout_padding_and_spacing(
1157    layout_geometry: &crate::layout::LayoutGeometry,
1158    orientation: Orientation,
1159    ctx: &ExpressionLoweringCtx,
1160) -> (llr_Expression, llr_Expression) {
1161    let padding_prop = |expr| {
1162        if let Some(expr) = expr {
1163            llr_Expression::PropertyReference(ctx.map_property_reference(expr))
1164        } else {
1165            llr_Expression::NumberLiteral(0.)
1166        }
1167    };
1168    let spacing = padding_prop(layout_geometry.spacing.orientation(orientation));
1169    let (begin, end) = layout_geometry.padding.begin_end(orientation);
1170
1171    let padding = make_struct(
1172        BuiltinPrivateStruct::Padding,
1173        [("begin", Type::Float32, padding_prop(begin)), ("end", Type::Float32, padding_prop(end))],
1174    );
1175
1176    (padding, spacing)
1177}
1178
1179fn layout_geometry_size(
1180    rect: &crate::layout::LayoutRect,
1181    orientation: Orientation,
1182    ctx: &ExpressionLoweringCtx,
1183) -> llr_Expression {
1184    match rect.size_reference(orientation) {
1185        Some(nr) => llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
1186        None => llr_Expression::NumberLiteral(0.),
1187    }
1188}
1189
1190pub fn get_layout_info(
1191    elem: &ElementRc,
1192    ctx: &mut ExpressionLoweringCtx,
1193    constraints: &crate::layout::LayoutConstraints,
1194    orientation: Orientation,
1195) -> llr_Expression {
1196    let layout_info = if let Some(layout_info_prop) = &elem.borrow().layout_info_prop(orientation) {
1197        llr_Expression::PropertyReference(ctx.map_property_reference(layout_info_prop))
1198    } else {
1199        lower_expression(&crate::layout::implicit_layout_info_call(elem, orientation), ctx)
1200    };
1201
1202    if constraints.has_explicit_restrictions(orientation) {
1203        let store = llr_Expression::StoreLocalVariable {
1204            name: "layout_info".into(),
1205            value: layout_info.into(),
1206        };
1207        let ty = crate::typeregister::layout_info_type();
1208        let mut values = ty
1209            .fields
1210            .keys()
1211            .map(|p| {
1212                (
1213                    p.clone(),
1214                    llr_Expression::StructFieldAccess {
1215                        base: llr_Expression::ReadLocalVariable {
1216                            name: "layout_info".into(),
1217                            ty: ty.clone().into(),
1218                        }
1219                        .into(),
1220                        name: p.clone(),
1221                    },
1222                )
1223            })
1224            .collect::<BTreeMap<_, _>>();
1225
1226        for (nr, s) in constraints.for_each_restrictions(orientation) {
1227            values.insert(
1228                s.into(),
1229                llr_Expression::PropertyReference(ctx.map_property_reference(&nr)),
1230            );
1231        }
1232        llr_Expression::CodeBlock([store, llr_Expression::Struct { ty, values }].into())
1233    } else {
1234        layout_info
1235    }
1236}
1237
1238// Called for repeated components in a grid layout, to generate code to provide input for organize_grid_layout().
1239pub fn get_grid_layout_input_for_repeated(
1240    ctx: &mut ExpressionLoweringCtx,
1241    grid_cell: &GridLayoutCell,
1242) -> llr_Expression {
1243    let mut assignments = Vec::new();
1244
1245    fn convert_row_col_expr(expr: &RowColExpr, ctx: &ExpressionLoweringCtx) -> llr_Expression {
1246        match expr {
1247            RowColExpr::Literal(n) => llr_Expression::NumberLiteral((*n).into()),
1248            RowColExpr::Named(nr) => {
1249                llr_Expression::PropertyReference(ctx.map_property_reference(nr))
1250            }
1251            RowColExpr::Auto => llr_Expression::NumberLiteral(i_slint_common::ROW_COL_AUTO as _),
1252        }
1253    }
1254
1255    // Generate assignments to the `result` slice parameter: result[i] = struct { ... }
1256    let mut push_assignment =
1257        |i: usize, new_row_expr: &llr_Expression, grid_cell: &GridLayoutCell| {
1258            let row = convert_row_col_expr(&grid_cell.row_expr, &*ctx);
1259            let col = convert_row_col_expr(&grid_cell.col_expr, &*ctx);
1260            let rowspan = convert_row_col_expr(&grid_cell.rowspan_expr, &*ctx);
1261            let colspan = convert_row_col_expr(&grid_cell.colspan_expr, &*ctx);
1262            let value = make_struct(
1263                BuiltinPrivateStruct::GridLayoutInputData,
1264                [
1265                    ("new_row", Type::Bool, new_row_expr.clone()),
1266                    ("row", Type::Float32, row),
1267                    ("col", Type::Float32, col),
1268                    ("rowspan", Type::Float32, rowspan),
1269                    ("colspan", Type::Float32, colspan),
1270                ],
1271            );
1272            assignments.push(llr_Expression::SliceIndexAssignment {
1273                slice_name: SmolStr::new_static("result"),
1274                index: i,
1275                value: value.into(),
1276            });
1277        };
1278
1279    if let Some(child_items) = grid_cell.child_items.as_ref() {
1280        // Repeated Row
1281        let mut new_row_expr = llr_Expression::BoolLiteral(true);
1282        for (i, child_item) in child_items.iter().enumerate() {
1283            let child_element = child_item.element.borrow();
1284            let child_cell = child_element.grid_layout_cell.as_ref().unwrap().borrow();
1285            push_assignment(i, &new_row_expr, &child_cell);
1286            new_row_expr = llr_Expression::BoolLiteral(false);
1287        }
1288    } else {
1289        // Single repeated item
1290        // grid_cell.new_row is the static information from the slint file.
1291        // In practice, for repeated items within a row, whether we should start a new row
1292        // is more dynamic (e.g. if the previous item was in "if false"),
1293        // and tracked by a local variable "new_row" in the generated code.
1294        let new_row_expr = llr_Expression::ReadLocalVariable {
1295            name: SmolStr::new_static("new_row"),
1296            ty: Type::Bool,
1297        };
1298        push_assignment(0, &new_row_expr, grid_cell);
1299    }
1300
1301    llr_Expression::CodeBlock(assignments)
1302}
1303
1304fn compile_path(
1305    path: &crate::expression_tree::Path,
1306    ctx: &mut ExpressionLoweringCtx,
1307) -> llr_Expression {
1308    fn llr_path_elements(elements: Vec<llr_Expression>) -> llr_Expression {
1309        llr_Expression::Cast {
1310            from: llr_Expression::Array {
1311                element_ty: crate::typeregister::path_element_type(),
1312                values: elements,
1313                output: llr_ArrayOutput::Slice,
1314            }
1315            .into(),
1316            to: Type::PathData,
1317        }
1318    }
1319
1320    match path {
1321        crate::expression_tree::Path::Elements(elements) => {
1322            let converted_elements = elements
1323                .iter()
1324                .map(|element| {
1325                    let element_type = Rc::new(Struct {
1326                        fields: element
1327                            .element_type
1328                            .properties
1329                            .iter()
1330                            .map(|(k, v)| (k.clone(), v.ty.clone()))
1331                            .collect(),
1332                        name: StructName::BuiltinPrivate(
1333                            element
1334                                .element_type
1335                                .native_class
1336                                .builtin_struct
1337                                .clone()
1338                                .expect("path elements should have a native_type"),
1339                        ),
1340                    });
1341
1342                    llr_Expression::Struct {
1343                        ty: element_type,
1344                        values: element
1345                            .element_type
1346                            .properties
1347                            .iter()
1348                            .map(|(element_field_name, element_property)| {
1349                                (
1350                                    element_field_name.clone(),
1351                                    element.bindings.get(element_field_name).map_or_else(
1352                                        || {
1353                                            llr_Expression::default_value_for_type(
1354                                                &element_property.ty,
1355                                            )
1356                                            .unwrap()
1357                                        },
1358                                        |expr| lower_expression(&expr.borrow().expression, ctx),
1359                                    ),
1360                                )
1361                            })
1362                            .collect(),
1363                    }
1364                })
1365                .collect();
1366            llr_path_elements(converted_elements)
1367        }
1368        crate::expression_tree::Path::Events(events, points) => {
1369            if events.is_empty() || points.is_empty() {
1370                return llr_path_elements(Vec::new());
1371            }
1372
1373            let events: Vec<_> = events.iter().map(|event| lower_expression(event, ctx)).collect();
1374
1375            let event_type = events.first().unwrap().ty(ctx);
1376
1377            let points: Vec<_> = points.iter().map(|point| lower_expression(point, ctx)).collect();
1378
1379            let point_type = points.first().unwrap().ty(ctx);
1380
1381            llr_Expression::Cast {
1382                from: llr_Expression::Struct {
1383                    ty: Rc::new(Struct {
1384                        fields: IntoIterator::into_iter([
1385                            (SmolStr::new_static("events"), Type::Array(event_type.clone().into())),
1386                            (SmolStr::new_static("points"), Type::Array(point_type.clone().into())),
1387                        ])
1388                        .collect(),
1389                        name: StructName::None,
1390                    }),
1391                    values: IntoIterator::into_iter([
1392                        (
1393                            SmolStr::new_static("events"),
1394                            llr_Expression::Array {
1395                                element_ty: event_type,
1396                                values: events,
1397                                output: llr_ArrayOutput::Slice,
1398                            },
1399                        ),
1400                        (
1401                            SmolStr::new_static("points"),
1402                            llr_Expression::Array {
1403                                element_ty: point_type,
1404                                values: points,
1405                                output: llr_ArrayOutput::Slice,
1406                            },
1407                        ),
1408                    ])
1409                    .collect(),
1410                }
1411                .into(),
1412                to: Type::PathData,
1413            }
1414        }
1415        crate::expression_tree::Path::Commands(commands) => llr_Expression::Cast {
1416            from: lower_expression(commands, ctx).into(),
1417            to: Type::PathData,
1418        },
1419    }
1420}
1421
1422pub fn make_struct(
1423    name: impl Into<StructName>,
1424    it: impl IntoIterator<Item = (&'static str, Type, llr_Expression)>,
1425) -> llr_Expression {
1426    let mut fields = BTreeMap::<SmolStr, Type>::new();
1427    let mut values = BTreeMap::<SmolStr, llr_Expression>::new();
1428    for (name, ty, expr) in it {
1429        fields.insert(SmolStr::new(name), ty);
1430        values.insert(SmolStr::new(name), expr);
1431    }
1432
1433    llr_Expression::Struct { ty: Rc::new(Struct { fields, name: name.into() }), values }
1434}