Skip to main content

slint_interpreter/
eval_layout.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
4//! Dispatch for `Expression::ExtraBuiltinFunctionCall` — layout helper
5//! functions generated by the LLR's layout lowering pass.
6
7use crate::Value;
8use crate::eval::{EvalContext, eval_expression};
9use i_slint_compiler::layout::Orientation;
10use i_slint_compiler::llr::lower_layout_expression::{
11    MEASURE_KNOWN_H_LOCAL, MEASURE_KNOWN_W_LOCAL,
12};
13use i_slint_compiler::llr::{
14    BoxMeasureCell, Expression, FlexboxMeasureCell, FlexboxMeasureCellKind,
15};
16use i_slint_core::SharedVector;
17use i_slint_core::layout::{
18    BoxLayoutData, FlexboxLayoutData, FlexboxLayoutItemInfo, GridLayoutData, GridLayoutInputData,
19    LayoutInfo, LayoutItemInfo, Padding,
20};
21use i_slint_core::model::Model;
22use i_slint_core::slice::Slice;
23
24// ── Value → layout-type converters ──────────────────────────────────────────
25
26fn to_f32(v: &Value) -> f32 {
27    match v {
28        Value::Number(n) => *n as f32,
29        _ => 0.,
30    }
31}
32
33fn to_padding(v: &Value) -> Padding {
34    let Value::Struct(s) = v else { return Padding::default() };
35    let f = |k| match s.get_field(k) {
36        Some(Value::Number(n)) => *n as f32,
37        _ => 0.,
38    };
39    Padding { begin: f("begin"), end: f("end") }
40}
41
42fn to_enum<T: std::str::FromStr + Default>(v: &Value) -> T {
43    match v {
44        Value::EnumerationValue(_, n) => n.parse().unwrap_or_default(),
45        _ => T::default(),
46    }
47}
48
49fn to_cells(v: &Value) -> Vec<LayoutItemInfo> {
50    let Value::Model(m) = v else { return Vec::new() };
51    (0..m.row_count())
52        .filter_map(|i| {
53            let Value::Struct(s) = m.row_data(i)? else { return None };
54            let c = s.get_field("constraint")?;
55            Some(LayoutItemInfo {
56                constraint: c.clone().try_into().unwrap_or_default(),
57                // Only set for a box layout's cross-axis cells; absent means `auto`.
58                cross_axis_self_alignment: s
59                    .get_field("cross-axis-self-alignment")
60                    .map(to_enum)
61                    .unwrap_or_default(),
62            })
63        })
64        .collect()
65}
66
67/// Convert one `Value::Struct` produced by the LLR's flexbox lowering:
68/// a `FlexboxLayoutItemInfo` with a `constraint` and a nested `props` field.
69/// `Struct::get_field` normalizes identifiers, so the kebab-case keys the
70/// lowering emits match regardless of spelling.
71pub(crate) fn flexbox_item_info_from_struct(s: &crate::api::Struct) -> FlexboxLayoutItemInfo {
72    let constraint: LayoutInfo =
73        s.get_field("constraint").cloned().and_then(|v| v.try_into().ok()).unwrap_or_default();
74    let props = match s.get_field("props") {
75        Some(Value::Struct(p)) => flex_props_from_struct(p),
76        _ => Default::default(),
77    };
78    FlexboxLayoutItemInfo { constraint, props }
79}
80
81/// Convert one `Value::Struct` produced by the LLR's flexbox lowering for a
82/// `FlexItemProps`.
83pub(crate) fn flex_props_from_struct(
84    s: &crate::api::Struct,
85) -> i_slint_core::layout::FlexItemProps {
86    i_slint_core::layout::FlexItemProps {
87        cross_axis_self_alignment: s
88            .get_field("cross-axis-self-alignment")
89            .map(to_enum)
90            .unwrap_or_default(),
91        layout_order: match s.get_field("layout-order") {
92            Some(Value::Number(n)) => *n as i32,
93            _ => 0,
94        },
95    }
96}
97
98fn to_flex_props(v: &Value) -> Vec<i_slint_core::layout::FlexItemProps> {
99    let Value::Model(m) = v else { return Vec::new() };
100    (0..m.row_count())
101        .filter_map(|i| {
102            let Value::Struct(s) = m.row_data(i)? else { return None };
103            Some(flex_props_from_struct(&s))
104        })
105        .collect()
106}
107
108fn to_u32_vec(v: &Value) -> Vec<u32> {
109    let Value::Model(m) = v else { return Vec::new() };
110    (0..m.row_count())
111        .filter_map(|i| match m.row_data(i)? {
112            Value::Number(n) => Some(n as u32),
113            _ => None,
114        })
115        .collect()
116}
117
118fn to_grid_input_data(v: &Value) -> Vec<GridLayoutInputData> {
119    let Value::Model(m) = v else { return Vec::new() };
120    (0..m.row_count())
121        .filter_map(|i| {
122            let Value::Struct(s) = m.row_data(i)? else { return None };
123            let f = |k: &str| match s.get_field(k) {
124                Some(Value::Number(n)) => *n as f32,
125                _ => 0.,
126            };
127            Some(GridLayoutInputData {
128                new_row: matches!(s.get_field("new-row"), Some(Value::Bool(true))),
129                col: f("col"),
130                row: f("row"),
131                colspan: f("colspan"),
132                rowspan: f("rowspan"),
133            })
134        })
135        .collect()
136}
137
138fn to_array_of_u16(v: &Value) -> SharedVector<u16> {
139    match v {
140        Value::ArrayOfU16(v) => v.clone(),
141        _ => Default::default(),
142    }
143}
144
145fn to_dialog_roles(v: &Value) -> Vec<i_slint_core::items::DialogButtonRole> {
146    let Value::Model(m) = v else { return Vec::new() };
147    (0..m.row_count())
148        .filter_map(|i| match m.row_data(i)? {
149            Value::EnumerationValue(_, n) => n.parse().ok(),
150            _ => None,
151        })
152        .collect()
153}
154
155fn sf32(s: &crate::api::Struct, k: &str) -> f32 {
156    match s.get_field(k) {
157        Some(Value::Number(n)) => *n as f32,
158        _ => 0.,
159    }
160}
161
162// ── Dispatch ────────────────────────────────────────────────────────────────
163
164pub(crate) fn call_extra_builtin(
165    ctx: &mut EvalContext,
166    name: &str,
167    arguments: &[Expression],
168) -> Value {
169    let a: Vec<Value> = arguments.iter().map(|e| eval_expression(ctx, e)).collect();
170
171    match name {
172        "box_layout_info" => {
173            let c = to_cells(&a[0]);
174            i_slint_core::layout::box_layout_info(
175                Slice::from_slice(&c),
176                to_f32(&a[1]),
177                &to_padding(&a[2]),
178                to_enum(&a[3]),
179            )
180            .into()
181        }
182        "box_layout_info_ortho" => {
183            let c = to_cells(&a[0]);
184            i_slint_core::layout::box_layout_info_ortho(Slice::from_slice(&c), &to_padding(&a[1]))
185                .into()
186        }
187        "organize_dialog_button_layout" => {
188            let input = to_grid_input_data(&a[0]);
189            let roles = to_dialog_roles(&a[1]);
190            Value::ArrayOfU16(i_slint_core::layout::organize_dialog_button_layout(
191                Slice::from_slice(&input),
192                Slice::from_slice(&roles),
193            ))
194        }
195        "organize_grid_layout" => {
196            let (input, ri, rs) = (to_grid_input_data(&a[0]), to_u32_vec(&a[1]), to_u32_vec(&a[2]));
197            Value::ArrayOfU16(i_slint_core::layout::organize_grid_layout(
198                Slice::from_slice(&input),
199                Slice::from_slice(&ri),
200                Slice::from_slice(&rs),
201            ))
202        }
203        "grid_layout_info" => {
204            let (c, ri, rs) = (to_cells(&a[1]), to_u32_vec(&a[2]), to_u32_vec(&a[3]));
205            i_slint_core::layout::grid_layout_info(
206                to_array_of_u16(&a[0]),
207                Slice::from_slice(&c),
208                Slice::from_slice(&ri),
209                Slice::from_slice(&rs),
210                to_f32(&a[4]),
211                &to_padding(&a[5]),
212                to_enum(&a[6]),
213            )
214            .into()
215        }
216        "solve_grid_layout" => {
217            let (c, ri, rs) = (to_cells(&a[1]), to_u32_vec(&a[3]), to_u32_vec(&a[4]));
218            let Value::Struct(s) = &a[0] else { return Value::LayoutCache(Default::default()) };
219            Value::LayoutCache(i_slint_core::layout::solve_grid_layout(
220                &GridLayoutData {
221                    size: sf32(s, "size"),
222                    spacing: sf32(s, "spacing"),
223                    padding: s.get_field("padding").map(to_padding).unwrap_or_default(),
224                    organized_data: s
225                        .get_field("organized-data")
226                        .map(to_array_of_u16)
227                        .unwrap_or_default(),
228                },
229                Slice::from_slice(&c),
230                to_enum(&a[2]),
231                Slice::from_slice(&ri),
232                Slice::from_slice(&rs),
233            ))
234        }
235        "solve_box_layout" => {
236            let ri = to_u32_vec(&a[1]);
237            let Value::Struct(s) = &a[0] else { return Value::LayoutCache(Default::default()) };
238            let cells = s.get_field("cells").map(to_cells).unwrap_or_default();
239            Value::LayoutCache(i_slint_core::layout::solve_box_layout(
240                &BoxLayoutData {
241                    size: sf32(s, "size"),
242                    spacing: sf32(s, "spacing"),
243                    padding: s.get_field("padding").map(to_padding).unwrap_or_default(),
244                    alignment: s.get_field("alignment").map(to_enum).unwrap_or_default(),
245                    cells: Slice::from_slice(&cells),
246                },
247                Slice::from_slice(&ri),
248            ))
249        }
250        "solve_box_layout_ortho" => {
251            let ri = to_u32_vec(&a[1]);
252            let Value::Struct(s) = &a[0] else { return Value::LayoutCache(Default::default()) };
253            let cells = s.get_field("cells").map(to_cells).unwrap_or_default();
254            Value::LayoutCache(i_slint_core::layout::solve_box_layout_ortho(
255                &i_slint_core::layout::BoxLayoutOrthoData {
256                    size: sf32(s, "size"),
257                    padding: s.get_field("padding").map(to_padding).unwrap_or_default(),
258                    cross_axis_alignment: s
259                        .get_field("cross-axis-alignment")
260                        .map(to_enum)
261                        .unwrap_or_default(),
262                    cells: Slice::from_slice(&cells),
263                },
264                Slice::from_slice(&ri),
265            ))
266        }
267        "solve_flexbox_layout" => {
268            let ri = to_u32_vec(&a[1]);
269            let Value::Struct(s) = &a[0] else { return Value::LayoutCache(Default::default()) };
270            let (ch, cv) = (
271                s.get_field("cells-h").map(to_cells).unwrap_or_default(),
272                s.get_field("cells-v").map(to_cells).unwrap_or_default(),
273            );
274            let fp = s.get_field("flex-props").map(to_flex_props).unwrap_or_default();
275            Value::LayoutCache(i_slint_core::layout::solve_flexbox_layout(
276                &FlexboxLayoutData {
277                    width: sf32(s, "width"),
278                    height: sf32(s, "height"),
279                    spacing_h: sf32(s, "spacing_h"),
280                    spacing_v: sf32(s, "spacing_v"),
281                    padding_h: s.get_field("padding-h").map(to_padding).unwrap_or_default(),
282                    padding_v: s.get_field("padding-v").map(to_padding).unwrap_or_default(),
283                    alignment: s.get_field("alignment").map(to_enum).unwrap_or_default(),
284                    direction: s.get_field("direction").map(to_enum).unwrap_or_default(),
285                    cross_axis_line_alignment: s
286                        .get_field("cross-axis-line-alignment")
287                        .map(to_enum)
288                        .unwrap_or_default(),
289                    cross_axis_alignment: s
290                        .get_field("cross-axis-alignment")
291                        .map(to_enum)
292                        .unwrap_or_default(),
293                    flex_wrap: s.get_field("flex-wrap").map(to_enum).unwrap_or_default(),
294                    cells_h: Slice::from_slice(&ch),
295                    cells_v: Slice::from_slice(&cv),
296                    flex_props: Slice::from_slice(&fp),
297                },
298                Slice::from_slice(&ri),
299            ))
300        }
301        "flexbox_layout_info_main_axis" => {
302            let cells = to_cells(&a[0]);
303            i_slint_core::layout::flexbox_layout_info_main_axis(
304                Slice::from_slice(&cells),
305                to_f32(&a[1]),
306                &to_padding(&a[2]),
307                to_enum(&a[3]),
308            )
309            .into()
310        }
311        "flexbox_layout_unwrapped_main" => {
312            let cells = to_cells(&a[0]);
313            Value::Number(i_slint_core::layout::flexbox_layout_unwrapped_main(
314                Slice::from_slice(&cells),
315                to_f32(&a[1]),
316                &to_padding(&a[2]),
317            ) as f64)
318        }
319        "flexbox_layout_info_cross_axis" => {
320            let (ch, cv) = (to_cells(&a[0]), to_cells(&a[1]));
321            let fp = to_flex_props(&a[2]);
322            i_slint_core::layout::flexbox_layout_info_cross_axis(
323                Slice::from_slice(&ch),
324                Slice::from_slice(&cv),
325                Slice::from_slice(&fp),
326                to_f32(&a[3]),
327                to_f32(&a[4]),
328                &to_padding(&a[5]),
329                &to_padding(&a[6]),
330                to_enum(&a[7]),
331                to_enum(&a[8]),
332                to_enum(&a[9]),
333                to_f32(&a[10]),
334            )
335            .into()
336        }
337        other => unimplemented!("ExtraBuiltinFunctionCall `{other}`"),
338    }
339}
340
341fn eval_info(ctx: &mut EvalContext, e: &Expression) -> LayoutInfo {
342    eval_expression(ctx, e).try_into().unwrap_or_default()
343}
344
345/// One flexbox cell as seen by the measure callback, after expanding
346/// repeaters (a repeater contributes one entry per instance).
347struct FlatCell<'a> {
348    kind: FlatCellKind<'a>,
349    w4h_only: bool,
350}
351
352enum FlatCellKind<'a> {
353    Static {
354        h_info: &'a Expression,
355        v_info: &'a Expression,
356    },
357    Repeated(vtable::VRc<i_slint_core::item_tree::ItemTreeVTable, crate::instance::Instance>),
358    /// No constrained layout info: the pre-resolved sizes are already correct.
359    Fixed,
360}
361
362/// Flatten `measure_cells` into one entry per taffy cell. Static cells carry
363/// their `(h_info, v_info)` expressions; a repeater expands to one instance
364/// per row (re-measured through its own item tree at the assigned cross size).
365fn flatten_measure_cells<'a>(
366    ctx: &mut EvalContext,
367    measure_cells: &'a [FlexboxMeasureCell],
368) -> Vec<FlatCell<'a>> {
369    let mut flat: Vec<FlatCell> = Vec::with_capacity(measure_cells.len());
370    for item in measure_cells {
371        match &item.kind {
372            FlexboxMeasureCellKind::Static { h_info, v_info } => flat.push(FlatCell {
373                kind: FlatCellKind::Static { h_info, v_info },
374                w4h_only: item.w4h_only,
375            }),
376            FlexboxMeasureCellKind::Repeated(repeater) => {
377                if let Some(current) = ctx.current.as_ref() {
378                    let rep = &current.repeaters[repeater.repeater_index];
379                    rep.track_instance_changes();
380                    flat.extend(rep.instances_vec().into_iter().map(|instance| FlatCell {
381                        kind: FlatCellKind::Repeated(instance),
382                        w4h_only: item.w4h_only,
383                    }));
384                }
385            }
386            FlexboxMeasureCellKind::Fixed => {
387                flat.push(FlatCell { kind: FlatCellKind::Fixed, w4h_only: item.w4h_only })
388            }
389        }
390    }
391    flat
392}
393
394/// Measure callback body shared by the solve and cross-axis-info paths:
395/// re-evaluate the cell's perpendicular layout info with the
396/// `measure_known_w` / `measure_known_h` local set to the dimension taffy
397/// assigned (a dimension it did not assign, `known_* == false`, arrives
398/// pre-resolved to the cell's preferred size). A probe with neither dimension
399/// known measures the cell's free axis at the default size (see
400/// `FlexboxMeasureFn` in i-slint-core).
401fn measure_flexbox_cell(
402    ctx: &mut EvalContext,
403    flat: &[FlatCell],
404    index: usize,
405    w: f32,
406    h: f32,
407    known_w: bool,
408    known_h: bool,
409) -> (f32, f32) {
410    let Some(cell) = flat.get(index) else { return (w, h) };
411    // measure the height at the width `w`
412    let measure_height = |ctx: &mut EvalContext| match &cell.kind {
413        FlatCellKind::Static { v_info, .. } => {
414            let prev = ctx.locals.insert(MEASURE_KNOWN_W_LOCAL.into(), Value::Number(w as f64));
415            let info = eval_info(ctx, v_info);
416            crate::eval::restore_local(ctx, MEASURE_KNOWN_W_LOCAL, prev);
417            (w, info.preferred_bounded())
418        }
419        FlatCellKind::Repeated(instance) => (
420            w,
421            instance
422                .as_pin_ref()
423                .flexbox_layout_item_info_at_cross_width(w)
424                .constraint
425                .preferred_bounded(),
426        ),
427        FlatCellKind::Fixed => (w, h),
428    };
429    // measure the width at the height `h`
430    let measure_width = |ctx: &mut EvalContext| match &cell.kind {
431        FlatCellKind::Static { h_info, .. } => {
432            let prev = ctx.locals.insert(MEASURE_KNOWN_H_LOCAL.into(), Value::Number(h as f64));
433            let info = eval_info(ctx, h_info);
434            crate::eval::restore_local(ctx, MEASURE_KNOWN_H_LOCAL, prev);
435            (info.preferred_bounded(), h)
436        }
437        FlatCellKind::Repeated(instance) => (
438            instance
439                .as_pin_ref()
440                .flexbox_layout_item_info_at_cross_height(h)
441                .constraint
442                .preferred_bounded(),
443            h,
444        ),
445        FlatCellKind::Fixed => (w, h),
446    };
447    match (known_w, known_h) {
448        (true, true) => (w, h),
449        (true, false) => measure_height(ctx),
450        (false, true) => measure_width(ctx),
451        (false, false) => {
452            if cell.w4h_only {
453                measure_width(ctx)
454            } else {
455                measure_height(ctx)
456            }
457        }
458    }
459}
460
461/// Interpret [`Expression::SolveFlexboxLayoutWithMeasure`].
462pub(crate) fn solve_flexbox_layout_with_measure(ctx: &mut EvalContext, expr: &Expression) -> Value {
463    let Expression::SolveFlexboxLayoutWithMeasure { data, repeater_indices, measure_cells } = expr
464    else {
465        return Value::Void;
466    };
467    let ri = to_u32_vec(&eval_expression(ctx, repeater_indices));
468    let data = eval_expression(ctx, data);
469    let Value::Struct(s) = &data else { return Value::LayoutCache(Default::default()) };
470    let (ch, cv) = (
471        s.get_field("cells-h").map(to_cells).unwrap_or_default(),
472        s.get_field("cells-v").map(to_cells).unwrap_or_default(),
473    );
474    let fp = s.get_field("flex-props").map(to_flex_props).unwrap_or_default();
475
476    let flat = flatten_measure_cells(ctx, measure_cells);
477    let mut measure = |index: usize, w: f32, h: f32, known_w: bool, known_h: bool| {
478        measure_flexbox_cell(ctx, &flat, index, w, h, known_w, known_h)
479    };
480
481    Value::LayoutCache(i_slint_core::layout::solve_flexbox_layout_with_measure(
482        &FlexboxLayoutData {
483            width: sf32(s, "width"),
484            height: sf32(s, "height"),
485            spacing_h: sf32(s, "spacing_h"),
486            spacing_v: sf32(s, "spacing_v"),
487            padding_h: s.get_field("padding-h").map(to_padding).unwrap_or_default(),
488            padding_v: s.get_field("padding-v").map(to_padding).unwrap_or_default(),
489            alignment: s.get_field("alignment").map(to_enum).unwrap_or_default(),
490            direction: s.get_field("direction").map(to_enum).unwrap_or_default(),
491            cross_axis_line_alignment: s
492                .get_field("cross-axis-line-alignment")
493                .map(to_enum)
494                .unwrap_or_default(),
495            cross_axis_alignment: s
496                .get_field("cross-axis-alignment")
497                .map(to_enum)
498                .unwrap_or_default(),
499            flex_wrap: s.get_field("flex-wrap").map(to_enum).unwrap_or_default(),
500            cells_h: Slice::from_slice(&ch),
501            cells_v: Slice::from_slice(&cv),
502            flex_props: Slice::from_slice(&fp),
503        },
504        Slice::from_slice(&ri),
505        Some(&mut measure),
506    ))
507}
508
509/// Interpret [`Expression::BoxLayoutInfoOrthoWithMeasure`]: solve the box
510/// layout's main axis at the known cross-axis size, then fold the cells'
511/// cross-axis infos with `box_layout_info_ortho`, measuring each
512/// height-for-width (resp. width-for-height) cell at its solved main size.
513pub(crate) fn box_layout_info_ortho_with_measure(
514    ctx: &mut EvalContext,
515    expr: &Expression,
516) -> Value {
517    use i_slint_core::model::RepeatedItemTree;
518    let Expression::BoxLayoutInfoOrthoWithMeasure {
519        solve_data,
520        padding_ortho,
521        orientation,
522        measure_cells,
523    } = expr
524    else {
525        return Value::Void;
526    };
527    let known_size_local = match orientation {
528        Orientation::Vertical => MEASURE_KNOWN_W_LOCAL,
529        Orientation::Horizontal => MEASURE_KNOWN_H_LOCAL,
530    };
531    let data = eval_expression(ctx, solve_data);
532    let Value::Struct(s) = &data else { return LayoutInfo::default().into() };
533    let cells = s.get_field("cells").map(to_cells).unwrap_or_default();
534    let solved = i_slint_core::layout::solve_box_layout(
535        &BoxLayoutData {
536            size: sf32(s, "size"),
537            spacing: sf32(s, "spacing"),
538            padding: s.get_field("padding").map(to_padding).unwrap_or_default(),
539            alignment: s.get_field("alignment").map(to_enum).unwrap_or_default(),
540            cells: Slice::from_slice(&cells),
541        },
542        Slice::from_slice(&[]),
543    );
544    let solved_size = |cursor: usize| solved.as_slice().get(cursor * 2 + 1).copied().unwrap_or(0.);
545    let mut out_cells: Vec<LayoutItemInfo> = Vec::with_capacity(cells.len());
546    let mut cursor = 0usize;
547    for cell in measure_cells {
548        match cell {
549            BoxMeasureCell::Static { info } => {
550                let prev = ctx
551                    .locals
552                    .insert(known_size_local.into(), Value::Number(solved_size(cursor) as f64));
553                let constraint = eval_info(ctx, info);
554                crate::eval::restore_local(ctx, known_size_local, prev);
555                out_cells.push(LayoutItemInfo { constraint, ..Default::default() });
556                cursor += 1;
557            }
558            BoxMeasureCell::Repeated(repeater) => {
559                let Some(current) = ctx.current.as_ref() else {
560                    // Without an instance, the repeater's cell count is
561                    // unknown, so the later cells' solved sizes can't be
562                    // located either.
563                    debug_assert!(false, "measure pass evaluated without a current instance");
564                    return LayoutInfo::default().into();
565                };
566                let rep = &current.repeaters[repeater.repeater_index];
567                rep.track_instance_changes();
568                for instance in rep.instances_vec() {
569                    let info = match orientation {
570                        Orientation::Vertical => instance
571                            .as_pin_ref()
572                            .layout_item_info_at_cross_width(solved_size(cursor)),
573                        Orientation::Horizontal => instance
574                            .as_pin_ref()
575                            .layout_item_info_at_cross_height(solved_size(cursor)),
576                    };
577                    out_cells.push(info);
578                    cursor += 1;
579                }
580            }
581        }
582    }
583    i_slint_core::layout::box_layout_info_ortho(
584        Slice::from_slice(&out_cells),
585        &to_padding(&eval_expression(ctx, padding_ortho)),
586    )
587    .into()
588}
589
590/// Interpret [`Expression::FlexboxLayoutInfoCrossAxisWithMeasure`]: the
591/// `flexbox_layout_info_cross_axis` builtin plus the measure callback, so
592/// height-for-width cells are measured at the main-axis size taffy assigns
593/// them rather than at the container size the cells were pre-measured at.
594pub(crate) fn flexbox_layout_info_cross_axis_with_measure(
595    ctx: &mut EvalContext,
596    expr: &Expression,
597) -> Value {
598    let Expression::FlexboxLayoutInfoCrossAxisWithMeasure { arguments, measure_cells } = expr
599    else {
600        return Value::Void;
601    };
602    let a: Vec<Value> = arguments.iter().map(|e| eval_expression(ctx, e)).collect();
603    let (ch, cv) = (to_cells(&a[0]), to_cells(&a[1]));
604    let fp = to_flex_props(&a[2]);
605    let flat = flatten_measure_cells(ctx, measure_cells);
606    let mut measure = |index: usize, w: f32, h: f32, known_w: bool, known_h: bool| {
607        measure_flexbox_cell(ctx, &flat, index, w, h, known_w, known_h)
608    };
609    i_slint_core::layout::flexbox_layout_info_cross_axis_with_measure(
610        Slice::from_slice(&ch),
611        Slice::from_slice(&cv),
612        Slice::from_slice(&fp),
613        to_f32(&a[3]),
614        to_f32(&a[4]),
615        &to_padding(&a[5]),
616        &to_padding(&a[6]),
617        to_enum(&a[7]),
618        to_enum(&a[8]),
619        to_enum(&a[9]),
620        to_f32(&a[10]),
621        Some(&mut measure),
622    )
623    .into()
624}