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