1use std::collections::BTreeMap;
5use std::sync::Arc;
6
7use itertools::Either;
8use smol_str::SmolStr;
9
10use super::lower_to_item_tree::LoweredElement;
11use super::{GridLayoutRepeatedElement, LayoutRepeatedElement};
12use crate::expression_tree::MinMaxOp;
13use crate::langtype::{BuiltinStruct, EnumerationValue, Struct, Type};
14use crate::layout::{FlexboxAxisRelation, GridLayoutCell, Orientation, RowColExpr};
15use crate::llr::ArrayOutput as llr_ArrayOutput;
16use crate::llr::Expression as llr_Expression;
17use crate::llr::{BoxMeasureCell, FlexboxMeasureCell};
18use crate::namedreference::NamedReference;
19use crate::object_tree::ElementRc;
20
21use super::lower_expression::{ExpressionLoweringCtx, make_struct};
22
23fn empty_int32_slice() -> llr_Expression {
24 llr_Expression::Array {
25 element_ty: Type::Int32,
26 values: Vec::new(),
27 output: llr_ArrayOutput::Slice,
28 }
29}
30
31pub(super) fn compute_grid_layout_info(
32 layout_organized_data_prop: &NamedReference,
33 layout: &crate::layout::GridLayout,
34 o: Orientation,
35 ctx: &mut ExpressionLoweringCtx,
36 cross_axis_size_override: Option<&crate::expression_tree::Expression>,
37) -> llr_Expression {
38 let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
39 let organized_cells = ctx.map_property_reference(layout_organized_data_prop);
40 let constraints_result = grid_layout_cell_constraints(layout, o, ctx, cross_axis_size_override);
41 let orientation_literal = llr_Expression::EnumerationValue(EnumerationValue {
42 value: o as _,
43 enumeration: crate::typeregister::BUILTIN.enums.Orientation.clone(),
44 });
45
46 let sub_expression = llr_Expression::ExtraBuiltinFunctionCall {
47 function: "grid_layout_info".into(),
48 arguments: vec![
49 llr_Expression::PropertyReference(organized_cells),
50 constraints_result.cells,
51 if constraints_result.compute_cells.is_none() {
52 empty_int32_slice()
53 } else {
54 llr_Expression::ReadLocalVariable {
55 name: "repeated_indices".into(),
56 ty: Type::Array(Type::Int32.into()),
57 }
58 },
59 if constraints_result.compute_cells.is_none() {
60 empty_int32_slice()
61 } else {
62 llr_Expression::ReadLocalVariable {
63 name: "repeater_steps".into(),
64 ty: Type::Array(Type::Int32.into()),
65 }
66 },
67 spacing,
68 padding,
69 orientation_literal,
70 ],
71 return_ty: crate::typeregister::layout_info_type().into(),
72 };
73 match constraints_result.compute_cells {
74 Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
75 cells_variable,
76 repeater_indices_var_name: Some("repeated_indices".into()),
77 repeater_steps_var_name: Some("repeater_steps".into()),
78 elements,
79 orientation: o,
80 repeated_cross_size: None,
81 sub_expression: Box::new(sub_expression),
82 },
83 None => sub_expression,
84 }
85}
86
87fn box_layout_has_height_for_width_repeated_cell(layout: &crate::layout::BoxLayout) -> bool {
91 layout.elems.iter().any(|item| {
92 item.element.borrow().repeated.is_some() && cell_is_height_for_width(&item.element)
93 })
94}
95
96pub const MEASURE_KNOWN_W_LOCAL: &str = "measure_known_w";
101
102pub(super) fn compute_box_layout_info(
103 layout: &crate::layout::BoxLayout,
104 o: Orientation,
105 ctx: &mut ExpressionLoweringCtx,
106 cross_axis_size_override: Option<&crate::expression_tree::Expression>,
107) -> llr_Expression {
108 let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
109 if o == Orientation::Vertical
114 && layout.orientation == Orientation::Horizontal
115 && let Some(override_expr) = cross_axis_size_override
116 && box_layout_needs_measure(layout)
117 {
118 return compute_box_layout_info_ortho_with_measure(layout, ctx, override_expr, padding);
119 }
120 let adjusted_override = cross_axis_size_override
121 .map(|o_expr| subtract_padding(o_expr.clone(), &layout.geometry, o.orthogonal()));
122 let bld = box_layout_data(layout, o, ctx, adjusted_override.as_ref(), None, false, false);
123 let sub_expression = if o == layout.orientation {
124 llr_Expression::ExtraBuiltinFunctionCall {
125 function: "box_layout_info".into(),
126 arguments: vec![bld.cells, spacing, padding, bld.alignment],
127 return_ty: crate::typeregister::layout_info_type().into(),
128 }
129 } else {
130 llr_Expression::ExtraBuiltinFunctionCall {
131 function: "box_layout_info_ortho".into(),
132 arguments: vec![bld.cells, padding],
133 return_ty: crate::typeregister::layout_info_type().into(),
134 }
135 };
136 let repeated_cross_size = adjusted_override
140 .as_ref()
141 .filter(|_| {
142 o == Orientation::Vertical
143 && o == layout.orientation
144 && box_layout_has_height_for_width_repeated_cell(layout)
145 })
146 .map(|e| Box::new(super::lower_expression::lower_expression(e, ctx)));
147 match bld.compute_cells {
148 Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
149 cells_variable,
150 repeater_indices_var_name: None,
151 repeater_steps_var_name: None,
152 elements,
153 orientation: o,
154 repeated_cross_size,
155 sub_expression: Box::new(sub_expression),
156 },
157 None => sub_expression,
158 }
159}
160
161fn box_layout_needs_measure(layout: &crate::layout::BoxLayout) -> bool {
164 layout.elems.iter().any(|li| cell_is_height_for_width(&li.element))
165}
166
167fn box_measure_cells_for(
174 layout: &crate::layout::BoxLayout,
175 ctx: &mut ExpressionLoweringCtx,
176) -> Vec<BoxMeasureCell> {
177 layout
178 .elems
179 .iter()
180 .map(|li| {
181 let elem = &li.element;
182 if elem.borrow().repeated.is_some() {
183 let repeater_index =
184 match ctx.mapping.element_mapping.get(&elem.clone().into()).unwrap() {
185 LoweredElement::Repeated { repeated_index } => *repeated_index,
186 _ => panic!("repeated box layout element not lowered as Repeated"),
187 };
188 return BoxMeasureCell::Repeated(LayoutRepeatedElement {
189 repeater_index,
190 row_child_templates: None,
191 cross_width: None,
192 });
193 }
194 let measure_local = crate::expression_tree::Expression::ReadLocalVariable {
195 name: MEASURE_KNOWN_W_LOCAL.into(),
196 ty: Type::LogicalLength,
197 };
198 let info = cell_layout_info(
199 elem,
200 &li.constraints,
201 ctx,
202 Orientation::Vertical,
203 Some(&measure_local),
204 None,
205 false,
206 );
207 BoxMeasureCell::Static { info }
208 })
209 .collect()
210}
211
212fn compute_box_layout_info_ortho_with_measure(
215 layout: &crate::layout::BoxLayout,
216 ctx: &mut ExpressionLoweringCtx,
217 cross_axis_size: &crate::expression_tree::Expression,
218 padding_ortho: llr_Expression,
219) -> llr_Expression {
220 let main_o = layout.orientation;
221 let (padding_main, spacing_main) =
222 generate_layout_padding_and_spacing(&layout.geometry, main_o, ctx);
223 let bld = box_layout_data(layout, main_o, ctx, None, None, false, true);
224 let size = super::lower_expression::lower_expression(cross_axis_size, ctx);
225 let solve_data = make_struct(
226 BuiltinStruct::BoxLayoutData,
227 [
228 ("size", Type::Float32, size),
229 ("spacing", Type::Float32, spacing_main),
230 ("padding", padding_main.ty(ctx), padding_main),
231 (
232 "alignment",
233 Type::Enumeration(crate::typeregister::BUILTIN.enums.LayoutAlignment.clone()),
234 bld.alignment,
235 ),
236 ("cells", bld.cells.ty(ctx), bld.cells),
237 ],
238 );
239 let sub_expression = llr_Expression::BoxLayoutInfoOrthoWithMeasure {
240 solve_data: Box::new(solve_data),
241 padding_ortho: Box::new(padding_ortho),
242 measure_cells: box_measure_cells_for(layout, ctx),
243 };
244 match bld.compute_cells {
245 Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
246 cells_variable,
247 repeater_indices_var_name: None,
248 repeater_steps_var_name: None,
249 elements,
250 orientation: main_o,
251 repeated_cross_size: None,
252 sub_expression: Box::new(sub_expression),
253 },
254 None => sub_expression,
255 }
256}
257
258pub(super) fn organize_grid_layout(
259 layout: &crate::layout::GridLayout,
260 ctx: &mut ExpressionLoweringCtx,
261) -> llr_Expression {
262 let input_data = grid_layout_input_data(layout, ctx);
263
264 if let Some(button_roles) = &layout.dialog_button_roles {
265 let e = crate::typeregister::BUILTIN.enums.DialogButtonRole.clone();
266 let roles = button_roles
267 .iter()
268 .map(|r| {
269 llr_Expression::EnumerationValue(EnumerationValue {
270 value: e.values.iter().position(|x| x == r).unwrap() as _,
271 enumeration: e.clone(),
272 })
273 })
274 .collect();
275 let roles_expr = llr_Expression::Array {
276 element_ty: Type::Enumeration(e),
277 values: roles,
278 output: llr_ArrayOutput::Slice,
279 };
280 llr_Expression::ExtraBuiltinFunctionCall {
281 function: "organize_dialog_button_layout".into(),
282 arguments: vec![input_data.cells, roles_expr],
283 return_ty: Type::Array(Type::Int32.into()),
284 }
285 } else {
286 let sub_expression = llr_Expression::ExtraBuiltinFunctionCall {
287 function: "organize_grid_layout".into(),
288 arguments: vec![
289 input_data.cells,
290 if input_data.compute_cells.is_none() {
291 empty_int32_slice()
292 } else {
293 llr_Expression::ReadLocalVariable {
294 name: SmolStr::new_static("repeated_indices"),
295 ty: Type::Array(Type::Int32.into()),
296 }
297 },
298 if input_data.compute_cells.is_none() {
299 empty_int32_slice()
300 } else {
301 llr_Expression::ReadLocalVariable {
302 name: SmolStr::new_static("repeater_steps"),
303 ty: Type::Array(Type::Int32.into()),
304 }
305 },
306 ],
307 return_ty: Type::Array(Type::Int32.into()),
308 };
309 if let Some((cells_variable, elements)) = input_data.compute_cells {
310 llr_Expression::WithGridInputData {
311 cells_variable,
312 repeater_indices_var_name: SmolStr::new_static("repeated_indices"),
313 repeater_steps_var_name: SmolStr::new_static("repeater_steps"),
314 elements,
315 sub_expression: Box::new(sub_expression),
316 }
317 } else {
318 sub_expression
319 }
320 }
321}
322
323pub(super) fn solve_grid_layout(
324 layout_organized_data_prop: &NamedReference,
325 layout: &crate::layout::GridLayout,
326 o: Orientation,
327 ctx: &mut ExpressionLoweringCtx,
328) -> llr_Expression {
329 let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
330 let cells = ctx.map_property_reference(layout_organized_data_prop);
331 let size = layout_geometry_size(&layout.geometry.rect, o, ctx);
332 let orientation_expr = llr_Expression::EnumerationValue(EnumerationValue {
333 value: o as _,
334 enumeration: crate::typeregister::BUILTIN.enums.Orientation.clone(),
335 });
336 let data = make_struct(
337 BuiltinStruct::GridLayoutData,
338 [
339 ("size", Type::Float32, size),
340 ("spacing", Type::Float32, spacing),
341 ("padding", padding.ty(ctx), padding),
342 ("organized_data", Type::ArrayOfU16, llr_Expression::PropertyReference(cells)),
343 ],
344 );
345 let constraints_result = grid_layout_cell_constraints(layout, o, ctx, None);
346
347 match constraints_result.compute_cells {
348 Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
349 cells_variable: cells_variable.clone(),
350 repeater_indices_var_name: Some("repeated_indices".into()),
351 repeater_steps_var_name: Some("repeater_steps".into()),
352 elements,
353 orientation: o,
354 repeated_cross_size: None,
355 sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
356 function: "solve_grid_layout".into(),
357 arguments: vec![
358 data,
359 llr_Expression::ReadLocalVariable {
360 name: cells_variable.into(),
361 ty: constraints_result.cells.ty(ctx),
362 },
363 orientation_expr,
364 llr_Expression::ReadLocalVariable {
365 name: "repeated_indices".into(),
366 ty: Type::Array(Type::Int32.into()),
367 },
368 llr_Expression::ReadLocalVariable {
369 name: "repeater_steps".into(),
370 ty: Type::Array(Type::Int32.into()),
371 },
372 ],
373 return_ty: Type::LayoutCache,
374 }),
375 },
376 None => llr_Expression::ExtraBuiltinFunctionCall {
377 function: "solve_grid_layout".into(),
378 arguments: vec![
379 data,
380 constraints_result.cells,
381 orientation_expr,
382 empty_int32_slice(),
383 empty_int32_slice(),
384 ],
385 return_ty: Type::LayoutCache,
386 },
387 }
388}
389
390pub(super) fn solve_box_layout(
391 layout: &crate::layout::BoxLayout,
392 o: Orientation,
393 ctx: &mut ExpressionLoweringCtx,
394) -> llr_Expression {
395 let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
396 let cross_clamp =
405 (o != layout.orientation).then(|| layout_cross_content_size(layout)).flatten();
406 let bld = box_layout_data(layout, o, ctx, None, cross_clamp.as_ref(), true, false);
407 let repeated_cross_size = (o == layout.orientation && o == Orientation::Vertical)
412 .then(|| layout_cross_content_size(layout))
413 .flatten()
414 .filter(|_| box_layout_has_height_for_width_repeated_cell(layout))
415 .map(|e| Box::new(super::lower_expression::lower_expression(&e, ctx)));
416 let size = layout_geometry_size(&layout.geometry.rect, o, ctx);
417 let (data, function) = if o == layout.orientation {
418 let data = make_struct(
419 BuiltinStruct::BoxLayoutData,
420 [
421 ("size", Type::Float32, size),
422 ("spacing", Type::Float32, spacing),
423 ("padding", padding.ty(ctx), padding),
424 (
425 "alignment",
426 Type::Enumeration(crate::typeregister::BUILTIN.enums.LayoutAlignment.clone()),
427 bld.alignment,
428 ),
429 ("cells", bld.cells.ty(ctx), bld.cells),
430 ],
431 );
432 (data, "solve_box_layout")
433 } else {
434 let cross_axis_alignment_ty =
435 Type::Enumeration(crate::typeregister::BUILTIN.enums.CrossAxisAlignment.clone());
436 let cross_axis_alignment = if let Some(nr) = &layout.cross_alignment {
437 llr_Expression::PropertyReference(ctx.map_property_reference(nr))
438 } else {
439 let e = crate::typeregister::BUILTIN.enums.CrossAxisAlignment.clone();
440 llr_Expression::EnumerationValue(EnumerationValue {
441 value: e.default_value,
442 enumeration: e,
443 })
444 };
445 let data = make_struct(
446 BuiltinStruct::BoxLayoutOrthoData,
447 [
448 ("size", Type::Float32, size),
449 ("padding", padding.ty(ctx), padding),
450 ("cross_axis_alignment", cross_axis_alignment_ty, cross_axis_alignment),
451 ("cells", bld.cells.ty(ctx), bld.cells),
452 ],
453 );
454 (data, "solve_box_layout_ortho")
455 };
456 match bld.compute_cells {
457 Some((cells_variable, elements)) => llr_Expression::WithLayoutItemInfo {
458 cells_variable,
459 repeater_indices_var_name: Some("repeated_indices".into()),
460 repeater_steps_var_name: None,
461 elements,
462 orientation: o,
463 repeated_cross_size,
464 sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
465 function: function.into(),
466 arguments: vec![
467 data,
468 llr_Expression::ReadLocalVariable {
469 name: "repeated_indices".into(),
470 ty: Type::Array(Type::Int32.into()),
471 },
472 ],
473 return_ty: Type::LayoutCache,
474 }),
475 },
476 None => llr_Expression::ExtraBuiltinFunctionCall {
477 function: function.into(),
478 arguments: vec![data, empty_int32_slice()],
479 return_ty: Type::LayoutCache,
480 },
481 }
482}
483
484pub(super) fn solve_flexbox_layout(
485 layout: &crate::layout::FlexboxLayout,
486 ctx: &mut ExpressionLoweringCtx,
487) -> llr_Expression {
488 let (padding_h, spacing_h) =
489 generate_layout_padding_and_spacing(&layout.geometry, Orientation::Horizontal, ctx);
490 let (padding_v, spacing_v) =
491 generate_layout_padding_and_spacing(&layout.geometry, Orientation::Vertical, ctx);
492 let container_width_for_cells = if matches!(
497 layout.axis_relation(Orientation::Vertical),
498 crate::layout::FlexboxAxisRelation::MainAxis
499 ) {
500 layout.geometry.rect.width_reference.as_ref().map(|nr| {
501 subtract_padding(
502 crate::expression_tree::Expression::PropertyReference(nr.clone()),
503 &layout.geometry,
504 Orientation::Horizontal,
505 )
506 })
507 } else {
508 None
509 };
510 let fld = flexbox_layout_data(layout, ctx, container_width_for_cells.as_ref());
511 let width = layout_geometry_size(&layout.geometry.rect, Orientation::Horizontal, ctx);
512 let height = layout_geometry_size(&layout.geometry.rect, Orientation::Vertical, ctx);
513 let data = make_struct(
514 BuiltinStruct::FlexboxLayoutData,
515 [
516 ("width", Type::Float32, width),
517 ("height", Type::Float32, height),
518 ("spacing_h", Type::Float32, spacing_h),
519 ("spacing_v", Type::Float32, spacing_v),
520 ("padding_h", padding_h.ty(ctx), padding_h),
521 ("padding_v", padding_v.ty(ctx), padding_v),
522 (
523 "alignment",
524 Type::Enumeration(crate::typeregister::BUILTIN.enums.LayoutAlignment.clone()),
525 fld.alignment,
526 ),
527 (
528 "direction",
529 Type::Enumeration(
530 crate::typeregister::BUILTIN.enums.FlexboxLayoutDirection.clone(),
531 ),
532 fld.direction,
533 ),
534 (
535 "cross_axis_line_alignment",
536 Type::Enumeration(crate::typeregister::BUILTIN.enums.LayoutAlignment.clone()),
537 fld.cross_axis_line_alignment,
538 ),
539 (
540 "cross_axis_alignment",
541 Type::Enumeration(crate::typeregister::BUILTIN.enums.CrossAxisAlignment.clone()),
542 fld.cross_axis_alignment,
543 ),
544 (
545 "flex_wrap",
546 Type::Enumeration(crate::typeregister::BUILTIN.enums.FlexboxLayoutWrap.clone()),
547 fld.flex_wrap,
548 ),
549 ("cells_h", fld.cells_h.ty(ctx), fld.cells_h),
550 ("cells_v", fld.cells_v.ty(ctx), fld.cells_v),
551 ("flex_props", fld.flex_props.ty(ctx), fld.flex_props),
552 ],
553 );
554 let repeated_cross_width = container_width_for_cells
558 .as_ref()
559 .map(|e| Box::new(super::lower_expression::lower_expression(e, ctx)));
560 let needs_measure = flexbox_needs_measure(layout);
563 match fld.compute_cells {
564 Some((cells_h_var, cells_v_var, flex_var, elements)) => {
565 let repeated_indices = || llr_Expression::ReadLocalVariable {
566 name: "repeated_indices".into(),
567 ty: Type::Array(Type::Int32.into()),
568 };
569 let sub_expression = if needs_measure {
570 llr_Expression::SolveFlexboxLayoutWithMeasure {
571 data: Box::new(data),
572 repeater_indices: Box::new(repeated_indices()),
573 measure_cells: measure_cells_for(layout, ctx),
574 }
575 } else {
576 llr_Expression::ExtraBuiltinFunctionCall {
577 function: "solve_flexbox_layout".into(),
578 arguments: vec![data, repeated_indices()],
579 return_ty: Type::LayoutCache,
580 }
581 };
582 llr_Expression::WithFlexboxLayoutItemInfo {
583 cells_h_variable: cells_h_var,
584 cells_v_variable: cells_v_var,
585 flex_props_variable: Some(flex_var),
586 repeater_indices_var_name: Some("repeated_indices".into()),
587 elements,
588 repeated_cross_width,
589 sub_expression: Box::new(sub_expression),
590 }
591 }
592 None => {
593 if !needs_measure {
594 return llr_Expression::ExtraBuiltinFunctionCall {
595 function: "solve_flexbox_layout".into(),
596 arguments: vec![data, empty_int32_slice()],
597 return_ty: Type::LayoutCache,
598 };
599 }
600 llr_Expression::SolveFlexboxLayoutWithMeasure {
601 data: Box::new(data),
602 repeater_indices: Box::new(empty_int32_slice()),
603 measure_cells: measure_cells_for(layout, ctx),
604 }
605 }
606 }
607}
608
609fn cell_is_height_for_width(elem: &ElementRc) -> bool {
613 if elem.borrow().repeated.is_some() {
614 let root = elem.borrow().base_type.as_component().root_element.clone();
615 return is_height_for_width_cell(&root);
616 }
617 is_height_for_width_cell(elem)
618}
619
620fn flexbox_needs_measure(layout: &crate::layout::FlexboxLayout) -> bool {
623 layout.elems.iter().any(|li| cell_is_height_for_width(&li.element))
624}
625
626fn measure_cells_for(
634 layout: &crate::layout::FlexboxLayout,
635 ctx: &mut ExpressionLoweringCtx,
636) -> Vec<FlexboxMeasureCell> {
637 layout
638 .elems
639 .iter()
640 .map(|li| {
641 let elem = &li.element;
642 if elem.borrow().repeated.is_some() {
643 let repeater_index =
644 match ctx.mapping.element_mapping.get(&elem.clone().into()).unwrap() {
645 LoweredElement::Repeated { repeated_index } => *repeated_index,
646 _ => panic!("repeated flexbox element not lowered as Repeated"),
647 };
648 return FlexboxMeasureCell::Repeated(LayoutRepeatedElement {
649 repeater_index,
650 row_child_templates: None,
651 cross_width: None,
652 });
653 }
654 if !cell_is_height_for_width(elem) {
655 return FlexboxMeasureCell::Fixed;
656 }
657 let v_constraint = crate::expression_tree::Expression::ReadLocalVariable {
658 name: MEASURE_KNOWN_W_LOCAL.into(),
659 ty: Type::LogicalLength,
660 };
661 let v_info = get_flex_cell_layout_info(
662 elem,
663 ctx,
664 &li.constraints,
665 Orientation::Vertical,
666 Some(v_constraint),
667 );
668 FlexboxMeasureCell::Static { v_info }
669 })
670 .collect()
671}
672
673pub(super) fn compute_flexbox_layout_info(
674 layout: &crate::layout::FlexboxLayout,
675 orientation: Orientation,
676 ctx: &mut ExpressionLoweringCtx,
677 cross_axis_size_override: Option<&crate::expression_tree::Expression>,
678) -> llr_Expression {
679 let width_override = cross_axis_size_override
685 .filter(|_| orientation == Orientation::Vertical)
686 .map(|e| subtract_padding(e.clone(), &layout.geometry, Orientation::Horizontal));
687 let fld = flexbox_layout_data(layout, ctx, width_override.as_ref());
688
689 match layout.axis_relation(orientation) {
690 crate::layout::FlexboxAxisRelation::MainAxis => {
691 compute_flexbox_layout_info_for_direction(layout, orientation, false, fld, ctx, None)
692 }
693 crate::layout::FlexboxAxisRelation::CrossAxis => compute_flexbox_layout_info_for_direction(
694 layout,
695 orientation,
696 true,
697 fld,
698 ctx,
699 cross_axis_size_override,
700 ),
701 crate::layout::FlexboxAxisRelation::Unknown => {
702 let row_expr = compute_flexbox_layout_info_for_direction(
705 layout,
706 orientation,
707 orientation == Orientation::Vertical, fld.clone(),
709 ctx,
710 cross_axis_size_override,
711 );
712 let col_expr = compute_flexbox_layout_info_for_direction(
713 layout,
714 orientation,
715 orientation == Orientation::Horizontal, fld,
717 ctx,
718 cross_axis_size_override,
719 );
720
721 let direction_enum = crate::typeregister::BUILTIN.enums.FlexboxLayoutDirection.clone();
723 let direction_ref = llr_Expression::PropertyReference(
724 ctx.map_property_reference(layout.direction.as_ref().unwrap()),
725 );
726
727 let is_row_condition = llr_Expression::BinaryExpression {
728 lhs: Box::new(llr_Expression::BinaryExpression {
729 lhs: Box::new(direction_ref.clone()),
730 rhs: Box::new(llr_Expression::EnumerationValue(EnumerationValue {
731 value: 0, enumeration: direction_enum.clone(),
733 })),
734 op: '=',
735 }),
736 rhs: Box::new(llr_Expression::BinaryExpression {
737 lhs: Box::new(direction_ref),
738 rhs: Box::new(llr_Expression::EnumerationValue(EnumerationValue {
739 value: 1, enumeration: direction_enum,
741 })),
742 op: '=',
743 }),
744 op: '|',
745 };
746
747 llr_Expression::Condition {
748 condition: Box::new(is_row_condition),
749 true_expr: Box::new(row_expr),
750 false_expr: Box::new(col_expr),
751 }
752 }
753 }
754}
755
756fn compute_flexbox_layout_info_for_direction(
757 layout: &crate::layout::FlexboxLayout,
758 orientation: Orientation,
759 is_cross_axis: bool,
760 fld: FlexboxLayoutDataResult,
761 ctx: &mut ExpressionLoweringCtx,
762 cross_axis_size_override: Option<&crate::expression_tree::Expression>,
763) -> llr_Expression {
764 let (padding_h, spacing_h) =
765 generate_layout_padding_and_spacing(&layout.geometry, Orientation::Horizontal, ctx);
766 let (padding_v, spacing_v) =
767 generate_layout_padding_and_spacing(&layout.geometry, Orientation::Vertical, ctx);
768
769 if is_cross_axis {
770 let constraint_size = if let Some(override_expr) = cross_axis_size_override {
779 super::lower_expression::lower_expression(override_expr, ctx)
780 } else {
781 match orientation {
782 Orientation::Horizontal => llr_Expression::NumberLiteral(f32::MAX as f64),
783 Orientation::Vertical => {
784 layout_geometry_size(&layout.geometry.rect, Orientation::Horizontal, ctx)
785 }
786 }
787 };
788
789 let arguments = vec![
790 fld.cells_h,
791 fld.cells_v,
792 fld.flex_props,
793 spacing_h,
794 spacing_v,
795 padding_h,
796 padding_v,
797 fld.direction,
798 fld.alignment,
802 fld.flex_wrap,
803 constraint_size,
804 ];
805
806 let sub_expression = if flexbox_needs_measure(layout) {
812 llr_Expression::FlexboxLayoutInfoCrossAxisWithMeasure {
813 arguments,
814 measure_cells: measure_cells_for(layout, ctx),
815 }
816 } else {
817 llr_Expression::ExtraBuiltinFunctionCall {
818 function: "flexbox_layout_info_cross_axis".into(),
819 arguments,
820 return_ty: crate::typeregister::layout_info_type().into(),
821 }
822 };
823 match fld.compute_cells {
824 Some((cells_h_var, cells_v_var, flex_var, elements)) => {
825 llr_Expression::WithFlexboxLayoutItemInfo {
826 cells_h_variable: cells_h_var,
827 cells_v_variable: cells_v_var,
828 flex_props_variable: Some(flex_var),
829 repeater_indices_var_name: None,
830 elements,
831 repeated_cross_width: None,
833 sub_expression: Box::new(sub_expression),
834 }
835 }
836 None => sub_expression,
837 }
838 } else {
839 let (cells, spacing, padding) = match orientation {
841 Orientation::Horizontal => (fld.cells_h, spacing_h, padding_h),
842 Orientation::Vertical => (fld.cells_v, spacing_v, padding_v),
843 };
844
845 match fld.compute_cells {
846 Some((cells_h_var, cells_v_var, _flex_var, elements)) => {
847 let cells_var = match orientation {
848 Orientation::Horizontal => cells_h_var.clone(),
849 Orientation::Vertical => cells_v_var.clone(),
850 };
851 llr_Expression::WithFlexboxLayoutItemInfo {
852 cells_h_variable: cells_h_var,
853 cells_v_variable: cells_v_var,
854 flex_props_variable: None,
855 repeater_indices_var_name: None,
856 elements,
857 repeated_cross_width: None,
859 sub_expression: Box::new(llr_Expression::ExtraBuiltinFunctionCall {
860 function: "flexbox_layout_info_main_axis".into(),
861 arguments: vec![
862 llr_Expression::ReadLocalVariable {
863 name: cells_var.into(),
864 ty: Type::Array(Arc::new(
865 crate::typeregister::layout_item_info_type(),
866 )),
867 },
868 spacing,
869 padding,
870 fld.flex_wrap,
871 ],
872 return_ty: crate::typeregister::layout_info_type().into(),
873 }),
874 }
875 }
876 None => llr_Expression::ExtraBuiltinFunctionCall {
877 function: "flexbox_layout_info_main_axis".into(),
878 arguments: vec![cells, spacing, padding, fld.flex_wrap],
879 return_ty: crate::typeregister::layout_info_type().into(),
880 },
881 }
882 }
883}
884
885#[derive(Clone)]
886struct FlexboxLayoutDataResult {
887 alignment: llr_Expression,
888 direction: llr_Expression,
889 cross_axis_line_alignment: llr_Expression,
890 cross_axis_alignment: llr_Expression,
891 flex_wrap: llr_Expression,
892 cells_h: llr_Expression,
893 cells_v: llr_Expression,
894 flex_props: llr_Expression,
897 compute_cells: Option<(
901 String,
902 String,
903 String,
904 Vec<Either<(llr_Expression, llr_Expression, llr_Expression), LayoutRepeatedElement>>,
905 )>,
906}
907
908fn flexbox_layout_data(
909 layout: &crate::layout::FlexboxLayout,
910 ctx: &mut ExpressionLoweringCtx,
911 width_override: Option<&crate::expression_tree::Expression>,
912) -> FlexboxLayoutDataResult {
913 let alignment = if let Some(expr) = &layout.geometry.alignment {
914 llr_Expression::PropertyReference(ctx.map_property_reference(expr))
915 } else {
916 let e = crate::typeregister::BUILTIN.enums.LayoutAlignment.clone();
917 llr_Expression::EnumerationValue(EnumerationValue {
918 value: e.default_value,
919 enumeration: e,
920 })
921 };
922
923 let direction = if let Some(expr) = &layout.direction {
924 llr_Expression::PropertyReference(ctx.map_property_reference(expr))
925 } else {
926 let e = crate::typeregister::BUILTIN.enums.FlexboxLayoutDirection.clone();
927 llr_Expression::EnumerationValue(EnumerationValue {
928 value: e.default_value,
929 enumeration: e,
930 })
931 };
932
933 let cross_axis_line_alignment = if let Some(expr) = &layout.cross_axis_line_alignment {
934 llr_Expression::PropertyReference(ctx.map_property_reference(expr))
935 } else {
936 let e = crate::typeregister::BUILTIN.enums.LayoutAlignment.clone();
937 llr_Expression::EnumerationValue(EnumerationValue {
938 value: e.default_value,
939 enumeration: e,
940 })
941 };
942
943 let cross_axis_alignment = if let Some(expr) = &layout.cross_axis_alignment {
944 llr_Expression::PropertyReference(ctx.map_property_reference(expr))
945 } else {
946 let e = crate::typeregister::BUILTIN.enums.CrossAxisAlignment.clone();
947 llr_Expression::EnumerationValue(EnumerationValue {
948 value: e.default_value,
949 enumeration: e,
950 })
951 };
952
953 let flex_wrap = if let Some(expr) = &layout.flex_wrap {
954 llr_Expression::PropertyReference(ctx.map_property_reference(expr))
955 } else {
956 let e = crate::typeregister::BUILTIN.enums.FlexboxLayoutWrap.clone();
957 llr_Expression::EnumerationValue(EnumerationValue {
958 value: e.default_value,
959 enumeration: e,
960 })
961 };
962
963 let repeater_count =
964 layout.elems.iter().filter(|i| i.element.borrow().repeated.is_some()).count();
965
966 let cell_ty = crate::typeregister::layout_item_info_type();
967 let flex_props_ty = crate::typeregister::flex_item_props_type();
968
969 let flex_prop =
970 |li: &crate::layout::LayoutItem, ctx: &mut ExpressionLoweringCtx| -> FlexItemProps {
971 FlexItemProps {
972 align_self: li
973 .cross_axis_self_alignment
974 .as_ref()
975 .map(|nr| llr_Expression::PropertyReference(ctx.map_property_reference(nr)))
976 .unwrap_or(default_align_self().1),
977 order: li
978 .layout_order
979 .as_ref()
980 .map(|nr| llr_Expression::PropertyReference(ctx.map_property_reference(nr)))
981 .unwrap_or(llr_Expression::NumberLiteral(0.0)),
982 }
983 };
984
985 let cell_v_constraint = |elem: &ElementRc| -> Option<crate::expression_tree::Expression> {
991 if elem.borrow().inherited_layout_info_v_with_constraint().is_some() {
997 return Some(width_override.cloned().unwrap_or_else(|| {
998 crate::expression_tree::Expression::NumberLiteral(
999 f32::MAX as f64,
1000 crate::expression_tree::Unit::Px,
1001 )
1002 }));
1003 }
1004 if !is_height_for_width_cell(elem) {
1005 return None;
1006 }
1007 width_override.cloned().or_else(|| default_cross_axis_constraint(elem))
1008 };
1009 if repeater_count == 0 {
1010 let cells_h = llr_Expression::Array {
1011 values: layout
1012 .elems
1013 .iter()
1014 .map(|li| {
1015 let layout_info_h = get_flex_cell_layout_info(
1016 &li.element,
1017 ctx,
1018 &li.constraints,
1019 Orientation::Horizontal,
1020 None,
1021 );
1022 make_layout_cell_data_struct(layout_info_h, None, None)
1023 })
1024 .collect(),
1025 element_ty: cell_ty.clone(),
1026 output: llr_ArrayOutput::Slice,
1027 };
1028 let cells_v = llr_Expression::Array {
1033 values: layout
1034 .elems
1035 .iter()
1036 .map(|li| {
1037 let constraint = cell_v_constraint(&li.element);
1038 let layout_info_v = get_flex_cell_layout_info(
1039 &li.element,
1040 ctx,
1041 &li.constraints,
1042 Orientation::Vertical,
1043 constraint,
1044 );
1045 make_layout_cell_data_struct(layout_info_v, None, None)
1046 })
1047 .collect(),
1048 element_ty: cell_ty,
1049 output: llr_ArrayOutput::Slice,
1050 };
1051 let flex_props = llr_Expression::Array {
1052 values: layout
1053 .elems
1054 .iter()
1055 .map(|li| make_flex_props_struct(flex_prop(li, ctx)))
1056 .collect(),
1057 element_ty: flex_props_ty,
1058 output: llr_ArrayOutput::Slice,
1059 };
1060 FlexboxLayoutDataResult {
1061 alignment,
1062 direction,
1063 cross_axis_line_alignment,
1064 cross_axis_alignment,
1065 flex_wrap,
1066 cells_h,
1067 cells_v,
1068 flex_props,
1069 compute_cells: None,
1070 }
1071 } else {
1072 let mut elements = Vec::new();
1073 for item in &layout.elems {
1074 if item.element.borrow().repeated.is_some() {
1075 let repeater_index =
1076 match ctx.mapping.element_mapping.get(&item.element.clone().into()).unwrap() {
1077 LoweredElement::Repeated { repeated_index } => *repeated_index,
1078 _ => panic!(),
1079 };
1080 elements.push(Either::Right(LayoutRepeatedElement {
1081 repeater_index,
1082 row_child_templates: None,
1083 cross_width: None,
1084 }))
1085 } else {
1086 let layout_info_h = get_flex_cell_layout_info(
1088 &item.element,
1089 ctx,
1090 &item.constraints,
1091 Orientation::Horizontal,
1092 None,
1093 );
1094 let constraint = cell_v_constraint(&item.element);
1095 let layout_info_v = get_flex_cell_layout_info(
1096 &item.element,
1097 ctx,
1098 &item.constraints,
1099 Orientation::Vertical,
1100 constraint,
1101 );
1102 elements.push(Either::Left((
1103 make_layout_cell_data_struct(layout_info_h, None, None),
1104 make_layout_cell_data_struct(layout_info_v, None, None),
1105 make_flex_props_struct(flex_prop(item, ctx)),
1106 )));
1107 }
1108 }
1109 let cells_h = llr_Expression::ReadLocalVariable {
1110 name: "cells_h".into(),
1111 ty: Type::Array(Arc::new(crate::typeregister::layout_item_info_type())),
1112 };
1113 let cells_v = llr_Expression::ReadLocalVariable {
1114 name: "cells_v".into(),
1115 ty: Type::Array(Arc::new(crate::typeregister::layout_item_info_type())),
1116 };
1117 let flex_props = llr_Expression::ReadLocalVariable {
1118 name: "flex_props".into(),
1119 ty: Type::Array(Arc::new(crate::typeregister::flex_item_props_type())),
1120 };
1121 FlexboxLayoutDataResult {
1122 alignment,
1123 direction,
1124 cross_axis_line_alignment,
1125 cross_axis_alignment,
1126 flex_wrap,
1127 cells_h,
1128 cells_v,
1129 flex_props,
1130 compute_cells: Some((
1131 "cells_h".into(),
1132 "cells_v".into(),
1133 "flex_props".into(),
1134 elements,
1135 )),
1136 }
1137 }
1138}
1139
1140struct BoxLayoutDataResult {
1141 alignment: llr_Expression,
1142 cells: llr_Expression,
1143 compute_cells: Option<(String, Vec<Either<llr_Expression, LayoutRepeatedElement>>)>,
1146}
1147
1148fn default_align_self() -> (Type, llr_Expression) {
1149 let e = crate::typeregister::BUILTIN.enums.CrossAxisAlignment.clone();
1150 (
1151 Type::Enumeration(e.clone()),
1152 llr_Expression::EnumerationValue(EnumerationValue {
1153 value: e.default_value,
1154 enumeration: e,
1155 }),
1156 )
1157}
1158
1159fn make_layout_cell_data_struct(
1164 layout_info: llr_Expression,
1165 align_self: Option<llr_Expression>,
1166 order: Option<llr_Expression>,
1167) -> llr_Expression {
1168 let Type::Struct(ty) = crate::typeregister::layout_item_info_type() else { unreachable!() };
1169 let mut values = BTreeMap::<SmolStr, llr_Expression>::new();
1170 values.insert("constraint".into(), layout_info);
1171 if let Some(align_self) = align_self {
1172 values.insert("cross-axis-self-alignment".into(), align_self);
1173 }
1174 if let Some(order) = order {
1175 values.insert("layout-order".into(), order);
1176 }
1177 llr_Expression::Struct { ty, values }
1178}
1179
1180#[derive(Clone)]
1181struct FlexItemProps {
1182 align_self: llr_Expression,
1183 order: llr_Expression,
1184}
1185
1186fn make_flex_props_struct(fp: FlexItemProps) -> llr_Expression {
1187 let (align_self_ty, _) = default_align_self();
1188 make_struct(
1189 BuiltinStruct::FlexItemProps,
1190 [
1191 ("cross-axis-self-alignment", align_self_ty, fp.align_self),
1192 ("layout-order", Type::Int32, fp.order),
1193 ],
1194 )
1195}
1196
1197fn box_layout_data(
1198 layout: &crate::layout::BoxLayout,
1199 orientation: Orientation,
1200 ctx: &mut ExpressionLoweringCtx,
1201 cross_axis_size_override: Option<&crate::expression_tree::Expression>,
1202 cross_clamp: Option<&crate::expression_tree::Expression>,
1203 for_solve: bool,
1204 for_measure_solve: bool,
1205) -> BoxLayoutDataResult {
1206 let alignment = if let Some(expr) = &layout.geometry.alignment {
1207 llr_Expression::PropertyReference(ctx.map_property_reference(expr))
1208 } else {
1209 let e = crate::typeregister::BUILTIN.enums.LayoutAlignment.clone();
1210 llr_Expression::EnumerationValue(EnumerationValue {
1211 value: e.default_value,
1212 enumeration: e,
1213 })
1214 };
1215
1216 let repeater_count =
1217 layout.elems.iter().filter(|i| i.element.borrow().repeated.is_some()).count();
1218
1219 let element_ty = crate::typeregister::layout_item_info_type();
1220
1221 let cell_align_self = |li: &crate::layout::LayoutItem, ctx: &mut ExpressionLoweringCtx| {
1227 li.cross_axis_self_alignment
1228 .as_ref()
1229 .filter(|_| for_solve && orientation != layout.orientation)
1230 .map(|nr| llr_Expression::PropertyReference(ctx.map_property_reference(nr)))
1231 };
1232 let cell_order = |li: &crate::layout::LayoutItem, ctx: &mut ExpressionLoweringCtx| {
1236 li.layout_order
1237 .as_ref()
1238 .filter(|_| for_solve && orientation == layout.orientation)
1239 .map(|nr| llr_Expression::PropertyReference(ctx.map_property_reference(nr)))
1240 };
1241 if repeater_count == 0 {
1242 let cells = llr_Expression::Array {
1243 values: layout
1244 .elems
1245 .iter()
1246 .map(|li| {
1247 let layout_info = cell_layout_info(
1248 &li.element,
1249 &li.constraints,
1250 ctx,
1251 orientation,
1252 cross_axis_size_override,
1253 cross_clamp,
1254 for_measure_solve,
1255 );
1256 let align_self = cell_align_self(li, ctx);
1257 let order = cell_order(li, ctx);
1258 make_layout_cell_data_struct(layout_info, align_self, order)
1259 })
1260 .collect(),
1261 element_ty,
1262 output: llr_ArrayOutput::Slice,
1263 };
1264 BoxLayoutDataResult { alignment, cells, compute_cells: None }
1265 } else {
1266 let mut elements = Vec::new();
1267 for item in &layout.elems {
1268 if item.element.borrow().repeated.is_some() {
1269 let repeater_index =
1270 match ctx.mapping.element_mapping.get(&item.element.clone().into()).unwrap() {
1271 LoweredElement::Repeated { repeated_index } => *repeated_index,
1272 _ => panic!(),
1273 };
1274 elements.push(Either::Right(LayoutRepeatedElement {
1275 repeater_index,
1276 row_child_templates: None,
1277 cross_width: None,
1278 }))
1279 } else {
1280 let layout_info = cell_layout_info(
1281 &item.element,
1282 &item.constraints,
1283 ctx,
1284 orientation,
1285 cross_axis_size_override,
1286 cross_clamp,
1287 for_measure_solve,
1288 );
1289 let align_self = cell_align_self(item, ctx);
1290 let order = cell_order(item, ctx);
1291 elements.push(Either::Left(make_layout_cell_data_struct(
1292 layout_info,
1293 align_self,
1294 order,
1295 )));
1296 }
1297 }
1298 let cells = llr_Expression::ReadLocalVariable {
1299 name: "cells".into(),
1300 ty: Type::Array(Arc::new(crate::typeregister::layout_info_type().into())),
1301 };
1302 BoxLayoutDataResult { alignment, cells, compute_cells: Some(("cells".into(), elements)) }
1303 }
1304}
1305
1306fn cell_layout_info(
1312 elem: &ElementRc,
1313 constraints: &crate::layout::LayoutConstraints,
1314 ctx: &mut ExpressionLoweringCtx,
1315 orientation: Orientation,
1316 cross_axis_size_override: Option<&crate::expression_tree::Expression>,
1317 cross_clamp: Option<&crate::expression_tree::Expression>,
1318 for_measure_solve: bool,
1319) -> llr_Expression {
1320 let constraint = match orientation {
1321 Orientation::Vertical => cross_axis_size_override
1322 .filter(|_| is_height_for_width_cell(elem))
1323 .cloned()
1324 .or_else(|| {
1325 (for_measure_solve && is_height_for_width_cell(elem))
1326 .then(|| default_cross_axis_constraint(elem))
1327 .flatten()
1328 }),
1329 Orientation::Horizontal => None,
1330 };
1331 let layout_info = get_layout_info(elem, ctx, constraints, orientation, constraint);
1332 let has_explicit_preferred = match orientation {
1338 Orientation::Horizontal => constraints.preferred_width.is_some(),
1339 Orientation::Vertical => constraints.preferred_height.is_some(),
1340 };
1341 match cross_clamp {
1342 Some(available) if !has_explicit_preferred => {
1343 clamp_wrapping_flex_cross_preferred(layout_info, elem, orientation, available, ctx)
1344 }
1345 _ => layout_info,
1346 }
1347}
1348
1349fn flexbox_unwrapped_main_expr(
1353 layout: &crate::layout::FlexboxLayout,
1354 orientation: Orientation,
1355 ctx: &mut ExpressionLoweringCtx,
1356) -> llr_Expression {
1357 let (padding_h, spacing_h) =
1358 generate_layout_padding_and_spacing(&layout.geometry, Orientation::Horizontal, ctx);
1359 let (padding_v, spacing_v) =
1360 generate_layout_padding_and_spacing(&layout.geometry, Orientation::Vertical, ctx);
1361 let fld = flexbox_layout_data(layout, ctx, None);
1362 let (spacing, padding) = match orientation {
1363 Orientation::Horizontal => (spacing_h, padding_h),
1364 Orientation::Vertical => (spacing_v, padding_v),
1365 };
1366 let cell_array_ty = Type::Array(Arc::new(crate::typeregister::layout_item_info_type()));
1367 let cells_expr = match &fld.compute_cells {
1368 Some((cells_h_var, cells_v_var, _, _)) => {
1369 let cells_var = match orientation {
1370 Orientation::Horizontal => cells_h_var.clone(),
1371 Orientation::Vertical => cells_v_var.clone(),
1372 };
1373 llr_Expression::ReadLocalVariable { name: cells_var.into(), ty: cell_array_ty }
1374 }
1375 None => match orientation {
1376 Orientation::Horizontal => fld.cells_h.clone(),
1377 Orientation::Vertical => fld.cells_v.clone(),
1378 },
1379 };
1380 let call = llr_Expression::ExtraBuiltinFunctionCall {
1381 function: "flexbox_layout_unwrapped_main".into(),
1382 arguments: vec![cells_expr, spacing, padding],
1383 return_ty: Type::Float32,
1384 };
1385 match fld.compute_cells {
1386 Some((cells_h_variable, cells_v_variable, _, elements)) => {
1387 llr_Expression::WithFlexboxLayoutItemInfo {
1388 cells_h_variable,
1389 cells_v_variable,
1390 flex_props_variable: None,
1393 repeater_indices_var_name: None,
1394 elements,
1395 repeated_cross_width: None,
1397 sub_expression: Box::new(call),
1398 }
1399 }
1400 None => call,
1401 }
1402}
1403
1404fn clamp_wrapping_flex_cross_preferred(
1411 layout_info: llr_Expression,
1412 elem: &ElementRc,
1413 orientation: Orientation,
1414 available: &crate::expression_tree::Expression,
1415 ctx: &mut ExpressionLoweringCtx,
1416) -> llr_Expression {
1417 let Some(flex) = crate::layout::FlexboxLayout::from_element(elem) else {
1418 return layout_info;
1419 };
1420 let axis_relation = flex.axis_relation(orientation);
1421 if axis_relation == FlexboxAxisRelation::CrossAxis {
1424 return layout_info;
1425 }
1426
1427 let unwrapped = flexbox_unwrapped_main_expr(&flex, orientation, ctx);
1428 let available = super::lower_expression::lower_expression(available, ctx);
1429 let clamped = llr_Expression::MinMax {
1430 ty: Type::Float32,
1431 op: MinMaxOp::Min,
1432 lhs: Box::new(available),
1433 rhs: Box::new(unwrapped),
1434 };
1435
1436 let ty = crate::typeregister::layout_info_type();
1438 let store = llr_Expression::StoreLocalVariable {
1439 name: "layout_info".into(),
1440 value: layout_info.into(),
1441 };
1442 let stored =
1443 || llr_Expression::ReadLocalVariable { name: "layout_info".into(), ty: ty.clone().into() };
1444 let stored_field = |name: &str| llr_Expression::StructFieldAccess {
1445 base: Box::new(stored()),
1446 name: name.into(),
1447 };
1448 let new_preferred = match &flex.flex_wrap {
1451 Some(nr) => {
1452 let wrap_enum = crate::typeregister::BUILTIN.enums.FlexboxLayoutWrap.clone();
1453 let is_no_wrap = llr_Expression::BinaryExpression {
1454 lhs: Box::new(llr_Expression::PropertyReference(ctx.map_property_reference(nr))),
1455 rhs: Box::new(llr_Expression::EnumerationValue(EnumerationValue {
1456 value: 1, enumeration: wrap_enum,
1458 })),
1459 op: '=',
1460 };
1461 llr_Expression::Condition {
1462 condition: Box::new(is_no_wrap),
1463 true_expr: Box::new(stored_field("preferred")),
1464 false_expr: Box::new(clamped),
1465 }
1466 }
1467 None => clamped, };
1469
1470 let mut values =
1471 ty.fields.keys().map(|p| (p.clone(), stored_field(p))).collect::<BTreeMap<_, _>>();
1472 values.insert("preferred".into(), new_preferred);
1473 let clamped_struct = llr_Expression::Struct { ty: ty.clone(), values };
1474
1475 let result = match axis_relation {
1481 FlexboxAxisRelation::MainAxis => clamped_struct,
1482 FlexboxAxisRelation::CrossAxis => unreachable!("returned early above"),
1483 FlexboxAxisRelation::Unknown => {
1484 let direction_enum = crate::typeregister::BUILTIN.enums.FlexboxLayoutDirection.clone();
1485 let direction_ref = llr_Expression::PropertyReference(
1486 ctx.map_property_reference(flex.direction.as_ref().unwrap()),
1487 );
1488 let (main_a, main_b) = match orientation {
1492 Orientation::Horizontal => (0, 1),
1493 Orientation::Vertical => (2, 3),
1494 };
1495 let is_direction = |value: usize| llr_Expression::BinaryExpression {
1496 lhs: Box::new(direction_ref.clone()),
1497 rhs: Box::new(llr_Expression::EnumerationValue(EnumerationValue {
1498 value,
1499 enumeration: direction_enum.clone(),
1500 })),
1501 op: '=',
1502 };
1503 let main_is_cross = llr_Expression::BinaryExpression {
1504 lhs: Box::new(is_direction(main_a)),
1505 rhs: Box::new(is_direction(main_b)),
1506 op: '|',
1507 };
1508 llr_Expression::Condition {
1509 condition: Box::new(main_is_cross),
1510 true_expr: Box::new(clamped_struct),
1511 false_expr: Box::new(stored()),
1512 }
1513 }
1514 };
1515
1516 llr_Expression::CodeBlock([store, result].into())
1517}
1518
1519struct GridLayoutCellConstraintsResult {
1520 cells: llr_Expression,
1521 compute_cells: Option<(String, Vec<Either<llr_Expression, LayoutRepeatedElement>>)>,
1524}
1525
1526pub const GRID_MEASURE_REPEATER_INDEX_LOCAL: &str = "grid_measure_repeater_index";
1529
1530pub const GRID_MEASURE_CHILD_INDEX_LOCAL: &str = "grid_measure_child_index";
1533
1534pub enum GridMeasureIndex {
1538 Instance,
1540 RowChild,
1545}
1546
1547pub fn grid_measure_cross_width(
1555 ctx: &mut ExpressionLoweringCtx,
1556 elem: &ElementRc,
1557 index: GridMeasureIndex,
1558) -> Option<llr_Expression> {
1559 let comp = elem.borrow().base_type.as_component().clone();
1560 let root = &comp.root_element;
1561 if !root.borrow().has_inherited_layout_info_v_with_constraint() {
1562 return None;
1563 }
1564 let mut width = repeated_cell_width_binding(root)?;
1565 let crate::expression_tree::Expression::GridRepeaterCacheAccess {
1566 repeater_index,
1567 inner_repeater_index,
1568 ..
1569 } = width.ignore_debug_hooks_mut()
1570 else {
1571 return None;
1572 };
1573 let local = |name: &str| crate::expression_tree::Expression::ReadLocalVariable {
1574 name: name.into(),
1575 ty: Type::Int32,
1576 };
1577 match index {
1578 GridMeasureIndex::Instance => **repeater_index = local(GRID_MEASURE_REPEATER_INDEX_LOCAL),
1579 GridMeasureIndex::RowChild => {
1580 *inner_repeater_index = Some(Box::new(local(GRID_MEASURE_CHILD_INDEX_LOCAL)))
1581 }
1582 }
1583 Some(super::lower_expression::lower_expression(&width, ctx))
1584}
1585
1586fn repeated_cell_width_binding(root: &ElementRc) -> Option<crate::expression_tree::Expression> {
1590 let width = root.borrow().geometry_props.as_ref()?.width.clone();
1591 let expr = width.element().borrow().binding(width.name())?.expression.clone();
1592 Some(expr)
1593}
1594
1595fn grid_layout_cell_constraints(
1596 layout: &crate::layout::GridLayout,
1597 orientation: Orientation,
1598 ctx: &mut ExpressionLoweringCtx,
1599 cross_axis_size_override: Option<&crate::expression_tree::Expression>,
1600) -> GridLayoutCellConstraintsResult {
1601 let repeater_count =
1602 layout.elems.iter().filter(|i| i.item.element.borrow().repeated.is_some()).count();
1603
1604 let element_ty = crate::typeregister::layout_item_info_type();
1605
1606 if repeater_count == 0 {
1607 let cells = llr_Expression::Array {
1608 element_ty,
1609 values: layout
1610 .elems
1611 .iter()
1612 .map(|li| {
1613 let layout_info = cell_layout_info(
1614 &li.item.element,
1615 &li.item.constraints,
1616 ctx,
1617 orientation,
1618 cross_axis_size_override,
1619 None,
1620 false,
1621 );
1622 make_layout_cell_data_struct(layout_info, None, None)
1623 })
1624 .collect(),
1625 output: llr_ArrayOutput::Slice,
1626 };
1627 GridLayoutCellConstraintsResult { cells, compute_cells: None }
1628 } else {
1629 let mut elements = Vec::new();
1630 for item in &layout.elems {
1631 if item.item.element.borrow().repeated.is_some() {
1632 let repeater_index = match ctx
1633 .mapping
1634 .element_mapping
1635 .get(&item.item.element.clone().into())
1636 .unwrap()
1637 {
1638 LoweredElement::Repeated { repeated_index } => *repeated_index,
1639 _ => panic!(),
1640 };
1641 let row_child_templates = get_row_child_templates(&item.item.element, ctx);
1642 let cross_width = (orientation == Orientation::Vertical
1647 && cross_axis_size_override.is_none()
1648 && row_child_templates.is_none())
1649 .then(|| {
1650 grid_measure_cross_width(ctx, &item.item.element, GridMeasureIndex::Instance)
1651 })
1652 .flatten();
1653 elements.push(Either::Right(LayoutRepeatedElement {
1654 repeater_index,
1655 row_child_templates,
1656 cross_width,
1657 }));
1658 } else {
1659 let layout_info = cell_layout_info(
1660 &item.item.element,
1661 &item.item.constraints,
1662 ctx,
1663 orientation,
1664 cross_axis_size_override,
1665 None,
1666 false,
1667 );
1668 elements.push(Either::Left(make_layout_cell_data_struct(layout_info, None, None)));
1669 }
1670 }
1671 let cells = llr_Expression::ReadLocalVariable {
1672 name: "cells".into(),
1673 ty: Type::Array(Arc::new(crate::typeregister::layout_info_type().into())),
1674 };
1675 GridLayoutCellConstraintsResult { cells, compute_cells: Some(("cells".into(), elements)) }
1676 }
1677}
1678
1679struct GridLayoutInputDataResult {
1680 cells: llr_Expression,
1681 compute_cells: Option<(String, Vec<Either<llr_Expression, GridLayoutRepeatedElement>>)>,
1684}
1685
1686fn grid_layout_input_data(
1688 layout: &crate::layout::GridLayout,
1689 ctx: &mut ExpressionLoweringCtx,
1690) -> GridLayoutInputDataResult {
1691 let propref = |named_ref: &RowColExpr| match named_ref {
1692 RowColExpr::Literal(n) => llr_Expression::NumberLiteral((*n).into()),
1693 RowColExpr::Named(nr) => llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
1694 RowColExpr::Auto => llr_Expression::NumberLiteral(i_slint_common::ROW_COL_AUTO as _),
1695 };
1696 let input_data_for_cell = |elem: &crate::layout::GridLayoutElement,
1697 new_row_expr: llr_Expression| {
1698 let row_expr = propref(&elem.cell.borrow().row_expr);
1699 let col_expr = propref(&elem.cell.borrow().col_expr);
1700 let rowspan_expr = propref(&elem.cell.borrow().rowspan_expr);
1701 let colspan_expr = propref(&elem.cell.borrow().colspan_expr);
1702
1703 make_struct(
1704 BuiltinStruct::GridLayoutInputData,
1705 [
1706 ("new_row", Type::Bool, new_row_expr),
1707 ("row", Type::Float32, row_expr),
1708 ("col", Type::Float32, col_expr),
1709 ("rowspan", Type::Float32, rowspan_expr),
1710 ("colspan", Type::Float32, colspan_expr),
1711 ],
1712 )
1713 };
1714 let repeater_count =
1715 layout.elems.iter().filter(|i| i.item.element.borrow().repeated.is_some()).count();
1716
1717 let element_ty = grid_layout_input_data_ty();
1718
1719 if repeater_count == 0 {
1720 let cells = llr_Expression::Array {
1721 element_ty,
1722 values: layout
1723 .elems
1724 .iter()
1725 .map(|elem| {
1726 input_data_for_cell(
1727 elem,
1728 llr_Expression::BoolLiteral(elem.cell.borrow().new_row),
1729 )
1730 })
1731 .collect(),
1732 output: llr_ArrayOutput::Slice,
1733 };
1734 GridLayoutInputDataResult { cells, compute_cells: None }
1735 } else {
1736 let mut elements = Vec::new();
1737 let mut after_repeater_in_same_row = false;
1738 for item in &layout.elems {
1739 let new_row = item.cell.borrow().new_row;
1740 if new_row {
1741 after_repeater_in_same_row = false;
1742 }
1743 if item.item.element.borrow().repeated.is_some() {
1744 let repeater_index = match ctx
1745 .mapping
1746 .element_mapping
1747 .get(&item.item.element.clone().into())
1748 .unwrap()
1749 {
1750 LoweredElement::Repeated { repeated_index } => *repeated_index,
1751 _ => panic!(),
1752 };
1753 let row_child_templates = get_row_child_templates(&item.item.element, ctx);
1754 let repeated_element =
1755 GridLayoutRepeatedElement { new_row, repeater_index, row_child_templates };
1756 elements.push(Either::Right(repeated_element));
1757 after_repeater_in_same_row = true;
1758 } else {
1759 let new_row_expr = if new_row || !after_repeater_in_same_row {
1760 llr_Expression::BoolLiteral(new_row)
1761 } else {
1762 llr_Expression::ReadLocalVariable {
1763 name: SmolStr::new_static("new_row"),
1764 ty: Type::Bool,
1765 }
1766 };
1767 elements.push(Either::Left(input_data_for_cell(item, new_row_expr)));
1768 }
1769 }
1770 let cells = llr_Expression::ReadLocalVariable {
1771 name: "cells".into(),
1772 ty: Type::Array(Arc::new(element_ty)),
1773 };
1774 GridLayoutInputDataResult { cells, compute_cells: Some(("cells".into(), elements)) }
1775 }
1776}
1777
1778pub(super) fn grid_layout_input_data_ty() -> Type {
1779 Type::Struct(Arc::new(Struct::new(
1780 IntoIterator::into_iter([
1781 (SmolStr::new_static("new_row"), Type::Bool),
1782 (SmolStr::new_static("row"), Type::Int32),
1783 (SmolStr::new_static("col"), Type::Int32),
1784 (SmolStr::new_static("rowspan"), Type::Int32),
1785 (SmolStr::new_static("colspan"), Type::Int32),
1786 ])
1787 .collect(),
1788 BuiltinStruct::GridLayoutInputData,
1789 )))
1790}
1791
1792fn generate_layout_padding_and_spacing(
1793 layout_geometry: &crate::layout::LayoutGeometry,
1794 orientation: Orientation,
1795 ctx: &ExpressionLoweringCtx,
1796) -> (llr_Expression, llr_Expression) {
1797 let padding_prop = |expr| {
1798 if let Some(expr) = expr {
1799 llr_Expression::PropertyReference(ctx.map_property_reference(expr))
1800 } else {
1801 llr_Expression::NumberLiteral(0.)
1802 }
1803 };
1804 let spacing = padding_prop(layout_geometry.spacing.orientation(orientation));
1805 let (begin, end) = layout_geometry.padding.begin_end(orientation);
1806
1807 let padding = make_struct(
1808 BuiltinStruct::Padding,
1809 [("begin", Type::Float32, padding_prop(begin)), ("end", Type::Float32, padding_prop(end))],
1810 );
1811
1812 (padding, spacing)
1813}
1814
1815fn is_height_for_width_cell(elem: &ElementRc) -> bool {
1825 let elem_b = elem.borrow();
1826
1827 let has_constrained_layoutinfo_v = elem_b.layout_info_v_with_constraint.is_some()
1830 || matches!(
1831 &elem_b.base_type,
1832 crate::langtype::ElementType::Component(base_comp)
1833 if base_comp.root_element.borrow().layout_info_v_with_constraint.is_some()
1834 );
1835 if has_constrained_layoutinfo_v {
1836 return true;
1837 }
1838
1839 if elem_b.effective_layout_info_prop(Orientation::Vertical).is_some() {
1840 return false;
1841 }
1842 drop(elem_b);
1843
1844 matches!(
1846 crate::layout::implicit_layout_info_call(
1847 elem,
1848 Orientation::Vertical,
1849 crate::layout::BuiltinFilter::All,
1850 None,
1851 ),
1852 Some(crate::expression_tree::Expression::FunctionCall { .. })
1853 )
1854}
1855
1856pub(crate) fn default_cross_axis_constraint(
1870 elem: &ElementRc,
1871) -> Option<crate::expression_tree::Expression> {
1872 let elem_b = elem.borrow();
1873
1874 if let Some(h_nr) = elem_b.effective_layout_info_prop(Orientation::Horizontal) {
1876 return Some(crate::expression_tree::Expression::StructFieldAccess {
1877 base: Box::new(crate::expression_tree::Expression::PropertyReference(h_nr.clone())),
1878 name: "preferred".into(),
1879 });
1880 }
1881 drop(elem_b);
1882
1883 crate::layout::implicit_layout_info_call(
1885 elem,
1886 Orientation::Horizontal,
1887 crate::layout::BuiltinFilter::All,
1888 None,
1889 )
1890 .map(|expr| crate::expression_tree::Expression::StructFieldAccess {
1891 base: Box::new(expr),
1892 name: "preferred".into(),
1893 })
1894}
1895
1896fn subtract_padding(
1900 base: crate::expression_tree::Expression,
1901 geometry: &crate::layout::LayoutGeometry,
1902 axis: Orientation,
1903) -> crate::expression_tree::Expression {
1904 use crate::expression_tree::Expression;
1905 let pads = match axis {
1906 Orientation::Horizontal => [&geometry.padding.left, &geometry.padding.right],
1907 Orientation::Vertical => [&geometry.padding.top, &geometry.padding.bottom],
1908 };
1909 let mut expr = base;
1910 for p in pads.into_iter().flatten() {
1911 expr = Expression::BinaryExpression {
1912 lhs: Box::new(expr),
1913 rhs: Box::new(Expression::PropertyReference(p.clone())),
1914 op: '-',
1915 source_location: None,
1916 };
1917 }
1918 expr
1919}
1920
1921fn layout_cross_content_size(
1924 layout: &crate::layout::BoxLayout,
1925) -> Option<crate::expression_tree::Expression> {
1926 use crate::expression_tree::Expression;
1927 let cross = layout.orientation.orthogonal();
1928 let size_nr = layout.geometry.rect.size_reference(cross)?.clone();
1929 Some(subtract_padding(Expression::PropertyReference(size_nr), &layout.geometry, cross))
1930}
1931
1932fn layout_geometry_size(
1933 rect: &crate::layout::LayoutRect,
1934 orientation: Orientation,
1935 ctx: &ExpressionLoweringCtx,
1936) -> llr_Expression {
1937 match rect.size_reference(orientation) {
1938 Some(nr) => llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
1939 None => llr_Expression::NumberLiteral(0.),
1940 }
1941}
1942
1943pub fn get_flex_cell_layout_info(
1954 elem: &ElementRc,
1955 ctx: &mut ExpressionLoweringCtx,
1956 constraints: &crate::layout::LayoutConstraints,
1957 orientation: Orientation,
1958 constraint: Option<crate::expression_tree::Expression>,
1959) -> llr_Expression {
1960 let effective = constraints.to_apply(elem, orientation);
1961 get_layout_info(elem, ctx, &effective, orientation, constraint)
1962}
1963
1964pub fn get_layout_info(
1965 elem: &ElementRc,
1966 ctx: &mut ExpressionLoweringCtx,
1967 constraints: &crate::layout::LayoutConstraints,
1968 orientation: Orientation,
1969 constraint: Option<crate::expression_tree::Expression>,
1970) -> llr_Expression {
1971 let layout_info = if let Some(c) = &constraint
1975 && orientation == Orientation::Vertical
1976 && let Some(parameterized_nr) = elem.borrow().layout_info_v_with_constraint.clone()
1977 {
1978 let call = crate::expression_tree::Expression::FunctionCall {
1979 function: crate::expression_tree::Callable::Function(parameterized_nr),
1980 arguments: vec![c.clone()],
1981 source_location: None,
1982 };
1983 super::lower_expression::lower_expression(&call, ctx)
1984 } else if let Some(layout_info_prop) = &elem.borrow().effective_layout_info_prop(orientation) {
1985 llr_Expression::PropertyReference(ctx.map_property_reference(layout_info_prop))
1986 } else {
1987 super::lower_expression::lower_expression(
1988 &crate::layout::implicit_layout_info_call(
1989 elem,
1990 orientation,
1991 crate::layout::BuiltinFilter::All,
1992 constraint,
1993 )
1994 .unwrap(),
1995 ctx,
1996 )
1997 };
1998
1999 if constraints.has_explicit_restrictions(orientation) {
2000 let store = llr_Expression::StoreLocalVariable {
2001 name: "layout_info".into(),
2002 value: layout_info.into(),
2003 };
2004 let ty = crate::typeregister::layout_info_type();
2005 let mut values = ty
2006 .fields
2007 .keys()
2008 .map(|p| {
2009 (
2010 p.clone(),
2011 llr_Expression::StructFieldAccess {
2012 base: llr_Expression::ReadLocalVariable {
2013 name: "layout_info".into(),
2014 ty: ty.clone().into(),
2015 }
2016 .into(),
2017 name: p.clone(),
2018 },
2019 )
2020 })
2021 .collect::<BTreeMap<_, _>>();
2022
2023 for (nr, s) in constraints.for_each_restrictions(orientation) {
2024 values.insert(
2025 s.into(),
2026 llr_Expression::PropertyReference(ctx.map_property_reference(nr)),
2027 );
2028 }
2029 llr_Expression::CodeBlock([store, llr_Expression::Struct { ty, values }].into())
2030 } else {
2031 layout_info
2032 }
2033}
2034
2035pub fn get_grid_layout_input_for_repeated(
2037 ctx: &mut ExpressionLoweringCtx,
2038 grid_cell: &GridLayoutCell,
2039) -> llr_Expression {
2040 let mut assignments = Vec::new();
2041
2042 fn convert_row_col_expr(expr: &RowColExpr, ctx: &ExpressionLoweringCtx) -> llr_Expression {
2043 match expr {
2044 RowColExpr::Literal(n) => llr_Expression::NumberLiteral((*n).into()),
2045 RowColExpr::Named(nr) => {
2046 llr_Expression::PropertyReference(ctx.map_property_reference(nr))
2047 }
2048 RowColExpr::Auto => llr_Expression::NumberLiteral(i_slint_common::ROW_COL_AUTO as _),
2049 }
2050 }
2051
2052 let mut push_assignment =
2054 |i: usize, new_row_expr: &llr_Expression, grid_cell: &GridLayoutCell| {
2055 let row = convert_row_col_expr(&grid_cell.row_expr, &*ctx);
2056 let col = convert_row_col_expr(&grid_cell.col_expr, &*ctx);
2057 let rowspan = convert_row_col_expr(&grid_cell.rowspan_expr, &*ctx);
2058 let colspan = convert_row_col_expr(&grid_cell.colspan_expr, &*ctx);
2059 let value = make_struct(
2060 BuiltinStruct::GridLayoutInputData,
2061 [
2062 ("new_row", Type::Bool, new_row_expr.clone()),
2063 ("row", Type::Float32, row),
2064 ("col", Type::Float32, col),
2065 ("rowspan", Type::Float32, rowspan),
2066 ("colspan", Type::Float32, colspan),
2067 ],
2068 );
2069 assignments.push(llr_Expression::SliceIndexAssignment {
2070 slice_name: SmolStr::new_static("result"),
2071 index: i,
2072 value: value.into(),
2073 });
2074 };
2075
2076 if let Some(child_items) = grid_cell.child_items.as_ref() {
2077 let mut new_row_expr = llr_Expression::BoolLiteral(true);
2080 let mut i = 0;
2081 for child_item in child_items.iter() {
2082 match child_item {
2083 crate::layout::RowChildTemplate::Static(layout_item) => {
2084 let child_element = layout_item.element.borrow();
2085 let child_cell = child_element.grid_layout_cell.as_ref().unwrap().borrow();
2086 push_assignment(i, &new_row_expr, &child_cell);
2087 new_row_expr = llr_Expression::BoolLiteral(false);
2088 i += 1;
2089 }
2090 crate::layout::RowChildTemplate::Repeated { .. } => {
2091 }
2093 }
2094 }
2095 } else {
2096 let new_row_expr = llr_Expression::ReadLocalVariable {
2102 name: SmolStr::new_static("new_row"),
2103 ty: Type::Bool,
2104 };
2105 push_assignment(0, &new_row_expr, grid_cell);
2106 }
2107
2108 llr_Expression::CodeBlock(assignments)
2109}
2110
2111fn get_row_child_templates(
2120 outer_element: &ElementRc,
2121 ctx: &ExpressionLoweringCtx,
2122) -> Option<Vec<super::RowChildTemplateInfo>> {
2123 let comp = outer_element.borrow().base_type.as_component().clone();
2124 ctx.state.row_child_templates(&comp)
2125}
2126
2127pub fn get_flexbox_layout_item_info_for_repeated(
2130 ctx: &mut ExpressionLoweringCtx,
2131 element: &ElementRc,
2132) -> llr_Expression {
2133 let prop_ref = |name: &'static str| -> Option<llr_Expression> {
2134 crate::layout::binding_reference(element, name)
2135 .map(|nr| llr_Expression::PropertyReference(ctx.map_property_reference(&nr)))
2136 };
2137
2138 let (_, align_self_default) = default_align_self();
2139
2140 let align_self = prop_ref("cross-axis-self-alignment").unwrap_or(align_self_default);
2141 let order = prop_ref("layout-order").unwrap_or(llr_Expression::NumberLiteral(0.0));
2142
2143 make_struct(
2144 BuiltinStruct::FlexboxLayoutItemInfo,
2145 [
2146 (
2147 "constraint",
2148 crate::typeregister::layout_info_type().into(),
2149 llr_Expression::default_value_for_type(
2150 &crate::typeregister::layout_info_type().into(),
2151 )
2152 .unwrap(),
2153 ),
2154 (
2155 "props",
2156 crate::typeregister::flex_item_props_type(),
2157 make_flex_props_struct(FlexItemProps { align_self, order }),
2158 ),
2159 ],
2160 )
2161}
2162
2163pub fn get_layout_info_v_constrained_for_repeated(
2169 ctx: &mut ExpressionLoweringCtx,
2170 element: &ElementRc,
2171 constraints: &crate::layout::LayoutConstraints,
2172) -> Option<llr_Expression> {
2173 if !is_height_for_width_cell(element) {
2174 return None;
2175 }
2176 let width_constraint = default_cross_axis_constraint(element).unwrap_or_else(|| {
2184 crate::expression_tree::Expression::NumberLiteral(
2185 f32::MAX as f64,
2186 crate::expression_tree::Unit::Px,
2187 )
2188 });
2189 Some(get_flex_cell_layout_info(
2190 element,
2191 ctx,
2192 constraints,
2193 Orientation::Vertical,
2194 Some(width_constraint),
2195 ))
2196}
2197
2198pub const CROSS_WIDTH_LOCAL: &str = "cross_width";
2203
2204pub fn get_layout_info_v_at_cross_width_for_repeated(
2217 ctx: &mut ExpressionLoweringCtx,
2218 element: &ElementRc,
2219 constraints: &crate::layout::LayoutConstraints,
2220 for_flex_cell: bool,
2221) -> Option<llr_Expression> {
2222 if !is_height_for_width_cell(element) {
2223 return None;
2224 }
2225 let width_constraint = crate::expression_tree::Expression::ReadLocalVariable {
2226 name: CROSS_WIDTH_LOCAL.into(),
2227 ty: Type::LogicalLength,
2228 };
2229 let get = if for_flex_cell { get_flex_cell_layout_info } else { get_layout_info };
2230 Some(get(element, ctx, constraints, Orientation::Vertical, Some(width_constraint)))
2231}