1use crate::Value;
5use crate::dynamic_item_tree::InstanceRef;
6use crate::eval::{self, EvalLocalContext};
7use i_slint_compiler::expression_tree::Expression;
8use i_slint_compiler::langtype::Type;
9use i_slint_compiler::layout::{
10 BoxLayout, GridLayout, LayoutConstraints, LayoutGeometry, Orientation, RowColExpr,
11};
12use i_slint_compiler::namedreference::NamedReference;
13use i_slint_compiler::object_tree::ElementRc;
14use i_slint_core::items::{DialogButtonRole, FlexboxLayoutDirection, ItemRc};
15use i_slint_core::layout::{self as core_layout, GridLayoutInputData, GridLayoutOrganizedData};
16use i_slint_core::model::RepeatedItemTree;
17use i_slint_core::slice::Slice;
18use i_slint_core::window::WindowAdapter;
19use std::rc::Rc;
20use std::str::FromStr;
21
22pub(crate) fn to_runtime(o: Orientation) -> core_layout::Orientation {
23 match o {
24 Orientation::Horizontal => core_layout::Orientation::Horizontal,
25 Orientation::Vertical => core_layout::Orientation::Vertical,
26 }
27}
28
29pub(crate) fn from_runtime(o: core_layout::Orientation) -> Orientation {
30 match o {
31 core_layout::Orientation::Horizontal => Orientation::Horizontal,
32 core_layout::Orientation::Vertical => Orientation::Vertical,
33 }
34}
35
36pub(crate) fn compute_grid_layout_info(
37 grid_layout: &GridLayout,
38 organized_data: &GridLayoutOrganizedData,
39 orientation: Orientation,
40 local_context: &mut EvalLocalContext,
41 cross_axis_size: Option<f32>,
42) -> Value {
43 let component = local_context.component_instance;
44 let expr_eval = |nr: &NamedReference| -> f32 {
45 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
46 };
47 let (padding, spacing) = padding_and_spacing(&grid_layout.geometry, orientation, &expr_eval);
48 let repeater_steps = grid_repeater_steps(grid_layout, local_context);
49 let repeater_indices = grid_repeater_indices(grid_layout, local_context, &repeater_steps);
50 let constraints = grid_layout_constraints(
51 grid_layout,
52 orientation,
53 local_context,
54 &repeater_steps,
55 cross_axis_size,
56 );
57 core_layout::grid_layout_info(
58 organized_data.clone(),
59 Slice::from_slice(constraints.as_slice()),
60 Slice::from_slice(repeater_indices.as_slice()),
61 Slice::from_slice(repeater_steps.as_slice()),
62 spacing,
63 &padding,
64 to_runtime(orientation),
65 )
66 .into()
67}
68
69pub(crate) fn compute_box_layout_info(
71 box_layout: &BoxLayout,
72 orientation: Orientation,
73 local_context: &mut EvalLocalContext,
74 cross_axis_size: Option<f32>,
75) -> Value {
76 let component = local_context.component_instance;
77 let expr_eval = |nr: &NamedReference| -> f32 {
78 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
79 };
80 let cross_axis_size = cross_axis_size.map(|w| {
81 let (cross_pad, _) =
82 padding_and_spacing(&box_layout.geometry, orientation.orthogonal(), &expr_eval);
83 w - cross_pad.begin - cross_pad.end
84 });
85 let (cells, alignment) = box_layout_data(
86 box_layout,
87 orientation,
88 component,
89 &expr_eval,
90 None,
91 cross_axis_size,
92 local_context,
93 None,
94 );
95 let (padding, spacing) = padding_and_spacing(&box_layout.geometry, orientation, &expr_eval);
96 if orientation == box_layout.orientation {
97 core_layout::box_layout_info(Slice::from(cells.as_slice()), spacing, &padding, alignment)
98 } else {
99 core_layout::box_layout_info_ortho(Slice::from(cells.as_slice()), &padding)
100 }
101 .into()
102}
103
104pub(crate) fn organize_grid_layout(
105 layout: &GridLayout,
106 local_context: &mut EvalLocalContext,
107) -> Value {
108 let repeater_steps = grid_repeater_steps(layout, local_context);
109 let cells = grid_layout_input_data(layout, local_context, &repeater_steps);
110 let repeater_indices = grid_repeater_indices(layout, local_context, &repeater_steps);
111 if let Some(buttons_roles) = &layout.dialog_button_roles {
112 let roles = buttons_roles
113 .iter()
114 .map(|r| DialogButtonRole::from_str(r).unwrap())
115 .collect::<Vec<_>>();
116 core_layout::organize_dialog_button_layout(
117 Slice::from_slice(cells.as_slice()),
118 Slice::from_slice(roles.as_slice()),
119 )
120 .into()
121 } else {
122 core_layout::organize_grid_layout(
123 Slice::from_slice(cells.as_slice()),
124 Slice::from_slice(repeater_indices.as_slice()),
125 Slice::from_slice(repeater_steps.as_slice()),
126 )
127 .into()
128 }
129}
130
131pub(crate) fn solve_grid_layout(
132 organized_data: &GridLayoutOrganizedData,
133 grid_layout: &GridLayout,
134 orientation: Orientation,
135 local_context: &mut EvalLocalContext,
136) -> Value {
137 let component = local_context.component_instance;
138 let expr_eval = |nr: &NamedReference| -> f32 {
139 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
140 };
141 let repeater_steps = grid_repeater_steps(grid_layout, local_context);
142 let repeater_indices = grid_repeater_indices(grid_layout, local_context, &repeater_steps);
143 let constraints =
144 grid_layout_constraints(grid_layout, orientation, local_context, &repeater_steps, None);
145
146 let (padding, spacing) = padding_and_spacing(&grid_layout.geometry, orientation, &expr_eval);
147 let size_ref = grid_layout.geometry.rect.size_reference(orientation);
148
149 let data = core_layout::GridLayoutData {
150 size: size_ref.map(expr_eval).unwrap_or(0.),
151 spacing,
152 padding,
153 organized_data: organized_data.clone(),
154 };
155
156 core_layout::solve_grid_layout(
157 &data,
158 Slice::from_slice(constraints.as_slice()),
159 to_runtime(orientation),
160 Slice::from_slice(repeater_indices.as_slice()),
161 Slice::from_slice(repeater_steps.as_slice()),
162 )
163 .into()
164}
165
166pub(crate) fn solve_box_layout(
167 box_layout: &BoxLayout,
168 orientation: Orientation,
169 local_context: &mut EvalLocalContext,
170) -> Value {
171 let component = local_context.component_instance;
172 let expr_eval = |nr: &NamedReference| -> f32 {
173 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
174 };
175
176 let mut repeated_indices = Vec::new();
177 let cross_axis_size = (orientation == box_layout.orientation
182 && orientation == Orientation::Horizontal
183 && box_layout
184 .elems
185 .iter()
186 .any(|c| c.element.borrow().inherited_layout_info_h_with_constraint().is_some()))
187 .then(|| {
188 let cross = orientation.orthogonal();
189 box_layout.geometry.rect.size_reference(cross).map(&expr_eval).map(|s| {
190 let (pad, _) = padding_and_spacing(&box_layout.geometry, cross, &expr_eval);
191 s - pad.begin - pad.end
192 })
193 })
194 .flatten();
195 let available_cross = (orientation != box_layout.orientation)
200 .then(|| {
201 box_layout.geometry.rect.size_reference(orientation).map(&expr_eval).map(|s| {
202 let (pad, _) = padding_and_spacing(&box_layout.geometry, orientation, &expr_eval);
203 s - pad.begin - pad.end
204 })
205 })
206 .flatten();
207 let (cells, alignment) = box_layout_data(
208 box_layout,
209 orientation,
210 component,
211 &expr_eval,
212 Some(&mut repeated_indices),
213 cross_axis_size,
214 local_context,
215 available_cross,
216 );
217 let (padding, spacing) = padding_and_spacing(&box_layout.geometry, orientation, &expr_eval);
218 let size = box_layout.geometry.rect.size_reference(orientation).map(&expr_eval).unwrap_or(0.);
219 if orientation == box_layout.orientation {
220 core_layout::solve_box_layout(
221 &core_layout::BoxLayoutData {
222 size,
223 spacing,
224 padding,
225 alignment,
226 cells: Slice::from(cells.as_slice()),
227 },
228 Slice::from(repeated_indices.as_slice()),
229 )
230 .into()
231 } else {
232 let cross_axis_alignment = box_layout
233 .cross_alignment
234 .as_ref()
235 .map(|nr| {
236 eval::load_property(component, &nr.element(), nr.name())
237 .unwrap()
238 .try_into()
239 .unwrap_or_default()
240 })
241 .unwrap_or_default();
242 core_layout::solve_box_layout_ortho(
243 &core_layout::BoxLayoutOrthoData {
244 size,
245 padding,
246 cross_axis_alignment,
247 cells: Slice::from(cells.as_slice()),
248 },
249 Slice::from(repeated_indices.as_slice()),
250 )
251 .into()
252 }
253}
254
255pub(crate) fn solve_flexbox_layout(
256 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
257 local_context: &mut EvalLocalContext,
258) -> Value {
259 let component = local_context.component_instance;
260 let expr_eval = |nr: &NamedReference| -> f32 {
261 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
262 };
263
264 let width_ref = &flexbox_layout.geometry.rect.width_reference;
265 let height_ref = &flexbox_layout.geometry.rect.height_reference;
266 let direction = flexbox_layout_direction(flexbox_layout, local_context);
267
268 let container_width_for_cells = match direction {
272 i_slint_core::items::FlexboxLayoutDirection::Column
273 | i_slint_core::items::FlexboxLayoutDirection::ColumnReverse => {
274 width_ref.as_ref().map(|w| {
275 let (pad_h, _) = padding_and_spacing(
276 &flexbox_layout.geometry,
277 Orientation::Horizontal,
278 &expr_eval,
279 );
280 expr_eval(w) - pad_h.begin - pad_h.end
281 })
282 }
283 _ => None,
284 };
285
286 let (cells_h, cells_v, repeated_indices) = flexbox_layout_data(
287 flexbox_layout,
288 component,
289 &expr_eval,
290 local_context,
291 container_width_for_cells,
292 None,
293 );
294
295 let alignment = flexbox_layout
296 .geometry
297 .alignment
298 .as_ref()
299 .map_or(i_slint_core::items::LayoutAlignment::default(), |nr| {
300 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
301 });
302 let align_content = flexbox_layout
303 .align_content
304 .as_ref()
305 .map_or(i_slint_core::items::FlexboxLayoutAlignContent::default(), |nr| {
306 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
307 });
308 let cross_axis_alignment = flexbox_layout
309 .cross_axis_alignment
310 .as_ref()
311 .map_or(i_slint_core::items::CrossAxisAlignment::default(), |nr| {
312 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
313 });
314 let flex_wrap = flexbox_layout
315 .flex_wrap
316 .as_ref()
317 .map_or(i_slint_core::items::FlexboxLayoutWrap::default(), |nr| {
318 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
319 });
320
321 let (padding_h, spacing_h) =
322 padding_and_spacing(&flexbox_layout.geometry, Orientation::Horizontal, &expr_eval);
323 let (padding_v, spacing_v) =
324 padding_and_spacing(&flexbox_layout.geometry, Orientation::Vertical, &expr_eval);
325
326 let data = core_layout::FlexboxLayoutData {
327 width: width_ref.as_ref().map(&expr_eval).unwrap_or(0.),
328 height: height_ref.as_ref().map(&expr_eval).unwrap_or(0.),
329 spacing_h,
330 spacing_v,
331 padding_h,
332 padding_v,
333 alignment,
334 direction,
335 align_content,
336 cross_axis_alignment,
337 flex_wrap,
338 cells_h: Slice::from(cells_h.as_slice()),
339 cells_v: Slice::from(cells_v.as_slice()),
340 };
341 let ri = Slice::from(repeated_indices.as_slice());
342
343 let window_adapter = component.window_adapter();
344
345 struct ChildElem {
355 elem: ElementRc,
356 has_constrained_layoutinfo_v: bool,
357 has_constrained_layoutinfo_h: bool,
358 has_aggregated_info: bool,
365 }
366 let mut child_elems: Vec<Option<ChildElem>> = Vec::new();
367 for layout_elem in &flexbox_layout.elems {
368 if layout_elem.item.element.borrow().repeated.is_some() {
369 let component_vec = repeater_instances(component, &layout_elem.item.element);
370 for _ in 0..component_vec.len() {
371 child_elems.push(None);
372 }
373 } else {
374 let elem_b = layout_elem.item.element.borrow();
375 let has_constrained_layoutinfo_v =
376 elem_b.inherited_layout_info_v_with_constraint().is_some();
377 let has_constrained_layoutinfo_h =
378 elem_b.inherited_layout_info_h_with_constraint().is_some();
379 let has_aggregated_info = elem_b.layout_info_prop.is_some();
380 drop(elem_b);
381 child_elems.push(Some(ChildElem {
382 elem: layout_elem.item.element.clone(),
383 has_constrained_layoutinfo_v,
384 has_constrained_layoutinfo_h,
385 has_aggregated_info,
386 }));
387 }
388 }
389
390 let mut measure = |child_index: usize,
391 known_w: Option<f32>,
392 known_h: Option<f32>|
393 -> (f32, f32) {
394 let default_w = cells_h.get(child_index).map_or(0., |c| c.constraint.preferred_bounded());
395 let default_h = cells_v.get(child_index).map_or(0., |c| c.constraint.preferred_bounded());
396 let w = known_w.unwrap_or(default_w);
397 let h = known_h.unwrap_or(default_h);
398
399 let ce = match child_elems.get(child_index) {
400 Some(Some(c)) => c,
401 _ => return (w, h),
402 };
403
404 let use_property_lookup = ce.has_aggregated_info
410 || ce.has_constrained_layoutinfo_v
411 || ce.has_constrained_layoutinfo_h;
412
413 if known_w.is_some() && known_h.is_none() {
414 if use_property_lookup {
415 let v_info = get_layout_info_with_constraint(
416 &ce.elem,
417 component,
418 &window_adapter,
419 Orientation::Vertical,
420 ce.has_constrained_layoutinfo_v.then_some(w),
421 );
422 return (w, v_info.preferred_bounded());
423 }
424 let elem_id = ce.elem.borrow().id.clone();
427 if let Some(item_within) = component.description.items.get(elem_id.as_str()) {
428 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
429 let item_rc =
430 ItemRc::new(vtable::VRc::into_dyn(item_comp), item_within.item_index());
431 let item = unsafe { item_within.item_from_item_tree(component.as_ptr()) };
432 let v_info = item.as_ref().layout_info(
433 to_runtime(Orientation::Vertical),
434 w,
435 &window_adapter,
436 &item_rc,
437 );
438 return (w, v_info.preferred_bounded());
439 }
440 return (w, h);
441 }
442 if known_h.is_some() && known_w.is_none() {
443 if use_property_lookup {
444 let h_info = get_layout_info_with_constraint(
445 &ce.elem,
446 component,
447 &window_adapter,
448 Orientation::Horizontal,
449 ce.has_constrained_layoutinfo_h.then_some(h),
450 );
451 return (h_info.preferred_bounded(), h);
452 }
453 let elem_id = ce.elem.borrow().id.clone();
454 if let Some(item_within) = component.description.items.get(elem_id.as_str()) {
455 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
456 let item_rc =
457 ItemRc::new(vtable::VRc::into_dyn(item_comp), item_within.item_index());
458 let item = unsafe { item_within.item_from_item_tree(component.as_ptr()) };
459 let h_info = item.as_ref().layout_info(
460 to_runtime(Orientation::Horizontal),
461 h,
462 &window_adapter,
463 &item_rc,
464 );
465 return (h_info.preferred_bounded(), h);
466 }
467 return (w, h);
468 }
469 (w, h)
470 };
471
472 core_layout::solve_flexbox_layout_with_measure(&data, ri, Some(&mut measure)).into()
473}
474
475fn flexbox_layout_direction(
476 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
477 local_context: &EvalLocalContext,
478) -> FlexboxLayoutDirection {
479 flexbox_layout
480 .direction
481 .as_ref()
482 .and_then(|nr| {
483 let value =
484 eval::load_property(local_context.component_instance, &nr.element(), nr.name())
485 .ok()?;
486 if let Value::EnumerationValue(_, variant) = &value {
487 match variant.as_str() {
488 "row" => Some(FlexboxLayoutDirection::Row),
489 "row-reverse" => Some(FlexboxLayoutDirection::RowReverse),
490 "column" => Some(FlexboxLayoutDirection::Column),
491 "column-reverse" => Some(FlexboxLayoutDirection::ColumnReverse),
492 _ => None,
493 }
494 } else {
495 None
496 }
497 })
498 .unwrap_or(FlexboxLayoutDirection::Row)
499}
500
501pub(crate) fn compute_flexbox_layout_info(
502 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
503 orientation: Orientation,
504 local_context: &mut EvalLocalContext,
505 cross_axis_size: Option<f32>,
506) -> Value {
507 let component = local_context.component_instance;
508 let expr_eval = |nr: &NamedReference| -> f32 {
509 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
510 };
511
512 let (width_override, height_override) = match orientation {
517 Orientation::Vertical => (cross_axis_size, None),
518 Orientation::Horizontal => (None, cross_axis_size),
519 };
520 let width_override = width_override.map(|w| {
523 let (pad_h, _) =
524 padding_and_spacing(&flexbox_layout.geometry, Orientation::Horizontal, &expr_eval);
525 w - pad_h.begin - pad_h.end
526 });
527 let height_override = height_override.map(|h| {
528 let (pad_v, _) =
529 padding_and_spacing(&flexbox_layout.geometry, Orientation::Vertical, &expr_eval);
530 h - pad_v.begin - pad_v.end
531 });
532 let (cells_h, cells_v, _repeated_indices) = flexbox_layout_data(
533 flexbox_layout,
534 component,
535 &expr_eval,
536 local_context,
537 width_override,
538 height_override,
539 );
540
541 let direction = flexbox_layout_direction(flexbox_layout, local_context);
543
544 let is_main_axis = matches!(
546 (direction, orientation),
547 (FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse, Orientation::Horizontal)
548 | (
549 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse,
550 Orientation::Vertical
551 )
552 );
553
554 let (padding_h, spacing_h) =
555 padding_and_spacing(&flexbox_layout.geometry, Orientation::Horizontal, &expr_eval);
556 let (padding_v, spacing_v) =
557 padding_and_spacing(&flexbox_layout.geometry, Orientation::Vertical, &expr_eval);
558
559 let flex_wrap = flexbox_layout
560 .flex_wrap
561 .as_ref()
562 .map_or(i_slint_core::items::FlexboxLayoutWrap::default(), |nr| {
563 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
564 });
565
566 if is_main_axis {
567 let (cells, spacing, padding) = match orientation {
568 Orientation::Horizontal => (&cells_h, spacing_h, &padding_h),
569 Orientation::Vertical => (&cells_v, spacing_v, &padding_v),
570 };
571 core_layout::flexbox_layout_info_main_axis(
572 Slice::from(cells.as_slice()),
573 spacing,
574 padding,
575 flex_wrap,
576 )
577 .into()
578 } else {
579 let constraint_size = cross_axis_size.unwrap_or_else(|| match orientation {
583 Orientation::Horizontal => {
584 let height_ref = &flexbox_layout.geometry.rect.height_reference;
585 height_ref.as_ref().map(&expr_eval).unwrap_or(0.)
586 }
587 Orientation::Vertical => {
588 let width_ref = &flexbox_layout.geometry.rect.width_reference;
589 width_ref.as_ref().map(&expr_eval).unwrap_or(0.)
590 }
591 });
592 core_layout::flexbox_layout_info_cross_axis(
593 Slice::from(cells_h.as_slice()),
594 Slice::from(cells_v.as_slice()),
595 spacing_h,
596 spacing_v,
597 &padding_h,
598 &padding_v,
599 direction,
600 flex_wrap,
601 constraint_size,
602 )
603 .into()
604 }
605}
606
607fn flexbox_layout_data(
608 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
609 component: InstanceRef,
610 expr_eval: &impl Fn(&NamedReference) -> f32,
611 _local_context: &mut EvalLocalContext,
612 width_override: Option<f32>,
613 height_override: Option<f32>,
614) -> (Vec<core_layout::FlexboxLayoutItemInfo>, Vec<core_layout::FlexboxLayoutItemInfo>, Vec<u32>) {
615 let window_adapter = component.window_adapter();
616 let mut cells_h = Vec::with_capacity(flexbox_layout.elems.len());
617 let mut cells_v = Vec::with_capacity(flexbox_layout.elems.len());
618 let mut repeated_indices = Vec::new();
619
620 struct ChildInfo {
623 flex_grow: f32,
624 flex_shrink: f32,
625 flex_basis: f32,
626 flex_align_self: i_slint_core::items::FlexboxLayoutAlignSelf,
627 flex_order: i32,
628 }
629 let mut static_children: Vec<Option<ChildInfo>> = Vec::new(); for layout_elem in &flexbox_layout.elems {
632 if layout_elem.item.element.borrow().repeated.is_some() {
633 let component_vec = repeater_instances(component, &layout_elem.item.element);
634 repeated_indices.push(cells_h.len() as u32);
635 repeated_indices.push(component_vec.len() as u32);
636 cells_h.extend(component_vec.iter().map(|x| {
637 x.as_pin_ref().flexbox_layout_item_info(to_runtime(Orientation::Horizontal), None)
638 }));
639 cells_v.extend(component_vec.iter().map(|x| {
640 x.as_pin_ref().flexbox_layout_item_info(to_runtime(Orientation::Vertical), None)
641 }));
642 for _ in 0..component_vec.len() {
643 static_children.push(None);
644 }
645 } else {
646 let h_constraint = layout_elem
652 .item
653 .element
654 .borrow()
655 .inherited_layout_info_h_with_constraint()
656 .is_some()
657 .then(|| height_override.unwrap_or(f32::MAX));
658 let mut layout_info_h = get_layout_info_with_constraint(
659 &layout_elem.item.element,
660 component,
661 &window_adapter,
662 Orientation::Horizontal,
663 h_constraint,
664 );
665 fill_layout_info_constraints(
666 &mut layout_info_h,
667 &layout_elem.item.constraints,
668 Orientation::Horizontal,
669 expr_eval,
670 );
671 let flex_grow = layout_elem.flex_grow.as_ref().map(&expr_eval).unwrap_or(0.0);
675 let flex_shrink = layout_elem.flex_shrink.as_ref().map(&expr_eval).unwrap_or(1.0);
676 let flex_basis = layout_elem.flex_basis.as_ref().map(&expr_eval).unwrap_or(-1.0);
677 let align_self = layout_elem
678 .align_self
679 .as_ref()
680 .map(|nr| {
681 eval::load_property(component, &nr.element(), nr.name())
682 .unwrap()
683 .try_into()
684 .unwrap()
685 })
686 .unwrap_or(i_slint_core::items::FlexboxLayoutAlignSelf::default());
687 let order = layout_elem.order.as_ref().map(expr_eval).unwrap_or(0.0) as i32;
688 cells_h.push(core_layout::FlexboxLayoutItemInfo {
689 constraint: layout_info_h,
690 flex_grow,
691 flex_shrink,
692 flex_basis,
693 flex_align_self: align_self,
694 flex_order: order,
695 });
696 cells_v.push(core_layout::FlexboxLayoutItemInfo::default());
698 static_children.push(Some(ChildInfo {
699 flex_grow,
700 flex_shrink,
701 flex_basis,
702 flex_align_self: align_self,
703 flex_order: order,
704 }));
705 }
706 }
707
708 let mut cell_idx = 0usize;
712 for layout_elem in &flexbox_layout.elems {
713 if layout_elem.item.element.borrow().repeated.is_some() {
714 let component_vec = repeater_instances(component, &layout_elem.item.element);
715 cell_idx += component_vec.len();
716 } else {
718 let width_constraint =
719 width_override.unwrap_or_else(|| cells_h[cell_idx].constraint.preferred_bounded());
720 let mut layout_info_v = get_layout_info_with_constraint(
721 &layout_elem.item.element,
722 component,
723 &window_adapter,
724 Orientation::Vertical,
725 Some(width_constraint),
726 );
727 fill_layout_info_constraints(
728 &mut layout_info_v,
729 &layout_elem.item.constraints,
730 Orientation::Vertical,
731 expr_eval,
732 );
733 if let Some(info) = &static_children[cell_idx] {
734 cells_v[cell_idx] = core_layout::FlexboxLayoutItemInfo {
735 constraint: layout_info_v,
736 flex_grow: info.flex_grow,
737 flex_shrink: info.flex_shrink,
738 flex_basis: info.flex_basis,
739 flex_align_self: info.flex_align_self,
740 flex_order: info.flex_order,
741 };
742 }
743 cell_idx += 1;
744 }
745 }
746
747 (cells_h, cells_v, repeated_indices)
748}
749
750fn padding_and_spacing(
752 layout_geometry: &LayoutGeometry,
753 orientation: Orientation,
754 expr_eval: &impl Fn(&NamedReference) -> f32,
755) -> (core_layout::Padding, f32) {
756 let spacing = layout_geometry.spacing.orientation(orientation).map_or(0., expr_eval);
757 let (begin, end) = layout_geometry.padding.begin_end(orientation);
758 let padding =
759 core_layout::Padding { begin: begin.map_or(0., expr_eval), end: end.map_or(0., expr_eval) };
760 (padding, spacing)
761}
762
763fn repeater_instances(
764 component: InstanceRef,
765 elem: &ElementRc,
766) -> Vec<crate::dynamic_item_tree::DynamicComponentVRc> {
767 generativity::make_guard!(guard);
768 let rep =
769 crate::dynamic_item_tree::get_repeater_by_name(component, elem.borrow().id.as_str(), guard);
770 rep.0.as_ref().track_instance_changes();
771 rep.0.as_ref().instances_vec()
772}
773
774fn grid_layout_input_data(
775 grid_layout: &i_slint_compiler::layout::GridLayout,
776 ctx: &EvalLocalContext,
777 repeater_steps: &[u32],
778) -> Vec<GridLayoutInputData> {
779 let component = ctx.component_instance;
780 let mut result = Vec::with_capacity(grid_layout.elems.len());
781 let mut after_repeater_in_same_row = false;
782 let mut new_row = true;
783 let mut repeater_idx = 0usize;
784 for elem in grid_layout.elems.iter() {
785 let eval_or_default = |expr: &RowColExpr, component: InstanceRef| match expr {
786 RowColExpr::Literal(value) => *value as f32,
787 RowColExpr::Auto => i_slint_common::ROW_COL_AUTO,
788 RowColExpr::Named(nr) => {
789 eval::load_property(component, &nr.element(), nr.name())
791 .unwrap()
792 .try_into()
793 .unwrap()
794 }
795 };
796
797 let cell_new_row = elem.cell.borrow().new_row;
798 if cell_new_row {
799 after_repeater_in_same_row = false;
800 }
801 if elem.item.element.borrow().repeated.is_some() {
802 let component_vec = repeater_instances(component, &elem.item.element);
803 new_row = cell_new_row;
804 for erased_sub_comp in &component_vec {
805 generativity::make_guard!(guard);
807 let sub_comp = erased_sub_comp.as_pin_ref();
808 let sub_instance_ref =
809 unsafe { InstanceRef::from_pin_ref(sub_comp.borrow(), guard) };
810
811 if let Some(children) = elem.cell.borrow().child_items.as_ref() {
812 new_row = true;
814 let start_count = result.len();
815
816 for child_template in children {
822 match child_template {
823 i_slint_compiler::layout::RowChildTemplate::Static(child_item) => {
824 let (row_val, col_val, rowspan_val, colspan_val) = {
825 let element_ref = child_item.element.borrow();
826 let child_cell =
827 element_ref.grid_layout_cell.as_ref().unwrap().borrow();
828 (
829 eval_or_default(&child_cell.row_expr, sub_instance_ref),
830 eval_or_default(&child_cell.col_expr, sub_instance_ref),
831 eval_or_default(&child_cell.rowspan_expr, sub_instance_ref),
832 eval_or_default(&child_cell.colspan_expr, sub_instance_ref),
833 )
834 };
835 result.push(GridLayoutInputData {
836 new_row,
837 col: col_val,
838 row: row_val,
839 colspan: colspan_val,
840 rowspan: rowspan_val,
841 });
842 new_row = false;
843 }
844 i_slint_compiler::layout::RowChildTemplate::Repeated {
845 repeated_element,
846 ..
847 } => {
848 let inner_root = repeated_element
851 .borrow()
852 .base_type
853 .as_component()
854 .root_element
855 .clone();
856 let (rowspan_expr, colspan_expr) = {
857 let element_ref = inner_root.borrow();
858 let child_cell =
859 element_ref.grid_layout_cell.as_ref().unwrap().borrow();
860 (
861 child_cell.rowspan_expr.clone(),
862 child_cell.colspan_expr.clone(),
863 )
864 };
865 let inner_instances =
866 repeater_instances(sub_instance_ref, repeated_element);
867 for (i, erased_inner) in inner_instances.iter().enumerate() {
868 generativity::make_guard!(inner_guard);
869 let inner_comp = erased_inner.as_pin_ref();
870 let inner_instance_ref = unsafe {
871 InstanceRef::from_pin_ref(inner_comp.borrow(), inner_guard)
872 };
873 result.push(GridLayoutInputData {
874 new_row: i == 0 && new_row,
875 rowspan: eval_or_default(&rowspan_expr, inner_instance_ref),
876 colspan: eval_or_default(&colspan_expr, inner_instance_ref),
877 ..Default::default()
878 });
879 }
880 if !inner_instances.is_empty() {
881 new_row = false;
882 }
883 }
884 }
885 }
886 let cells_pushed = result.len() - start_count;
888 let expected_step =
889 repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
890 for _ in cells_pushed..expected_step {
891 result.push(GridLayoutInputData::default());
892 }
893 } else {
894 let cell = elem.cell.borrow();
896 let row = eval_or_default(&cell.row_expr, sub_instance_ref);
897 let col = eval_or_default(&cell.col_expr, sub_instance_ref);
898 let rowspan = eval_or_default(&cell.rowspan_expr, sub_instance_ref);
899 let colspan = eval_or_default(&cell.colspan_expr, sub_instance_ref);
900 result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
901 new_row = false;
902 }
903 }
904 repeater_idx += 1;
905 after_repeater_in_same_row = true;
906 } else {
907 let new_row =
908 if cell_new_row || !after_repeater_in_same_row { cell_new_row } else { new_row };
909 let row = eval_or_default(&elem.cell.borrow().row_expr, component);
910 let col = eval_or_default(&elem.cell.borrow().col_expr, component);
911 let rowspan = eval_or_default(&elem.cell.borrow().rowspan_expr, component);
912 let colspan = eval_or_default(&elem.cell.borrow().colspan_expr, component);
913 result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
914 }
915 }
916 result
917}
918
919fn row_runtime_child_count(
923 child_items: &[i_slint_compiler::layout::RowChildTemplate],
924 sub_instance_ref: InstanceRef,
925) -> usize {
926 let mut count = 0;
927 for child in child_items {
928 if let Some(repeated_element) = child.repeated_element() {
929 count += repeater_instances(sub_instance_ref, repeated_element).len();
930 } else {
931 count += 1;
932 }
933 }
934 count
935}
936
937fn grid_repeater_indices(
938 grid_layout: &i_slint_compiler::layout::GridLayout,
939 ctx: &mut EvalLocalContext,
940 repeater_steps: &[u32],
941) -> Vec<u32> {
942 let component = ctx.component_instance;
943 let mut repeater_indices = Vec::new();
944 let mut num_cells = 0;
945 let mut step_idx = 0;
946 for elem in grid_layout.elems.iter() {
947 if elem.item.element.borrow().repeated.is_some() {
948 let component_vec = repeater_instances(component, &elem.item.element);
949 repeater_indices.push(num_cells as _);
950 repeater_indices.push(component_vec.len() as _);
951 let item_count = repeater_steps[step_idx] as usize;
952 num_cells += component_vec.len() * item_count;
953 step_idx += 1;
954 } else {
955 num_cells += 1;
956 }
957 }
958 repeater_indices
959}
960
961fn grid_repeater_steps(
962 grid_layout: &i_slint_compiler::layout::GridLayout,
963 ctx: &mut EvalLocalContext,
964) -> Vec<u32> {
965 let component = ctx.component_instance;
966 let mut repeater_steps = Vec::new();
967 for elem in grid_layout.elems.iter() {
968 if elem.item.element.borrow().repeated.is_some() {
969 let item_count = match &elem.cell.borrow().child_items {
970 Some(ci)
971 if ci.iter().any(i_slint_compiler::layout::RowChildTemplate::is_repeated) =>
972 {
973 let component_vec = repeater_instances(component, &elem.item.element);
975 component_vec
976 .iter()
977 .map(|sub| {
978 generativity::make_guard!(guard);
979 let sub_pin = sub.as_pin_ref();
980 let sub_ref =
981 unsafe { InstanceRef::from_pin_ref(sub_pin.borrow(), guard) };
982 row_runtime_child_count(ci, sub_ref)
983 })
984 .max()
985 .unwrap_or(0)
986 }
987 Some(ci) => ci.len(),
988 None => 1,
989 };
990 repeater_steps.push(item_count as u32);
991 }
992 }
993 repeater_steps
994}
995
996fn grid_layout_constraints(
997 grid_layout: &i_slint_compiler::layout::GridLayout,
998 orientation: Orientation,
999 ctx: &mut EvalLocalContext,
1000 repeater_steps: &[u32],
1001 cross_axis_size: Option<f32>,
1002) -> Vec<core_layout::LayoutItemInfo> {
1003 let component = ctx.component_instance;
1004 let expr_eval = |nr: &NamedReference| -> f32 {
1005 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
1006 };
1007 let mut constraints = Vec::with_capacity(grid_layout.elems.len());
1008
1009 let mut repeater_idx = 0usize;
1010 for layout_elem in grid_layout.elems.iter() {
1011 if layout_elem.item.element.borrow().repeated.is_some() {
1012 let component_vec = repeater_instances(component, &layout_elem.item.element);
1013 let child_items = layout_elem.cell.borrow().child_items.clone();
1014 let has_children = child_items.is_some();
1015 if has_children {
1016 let ci = child_items.as_ref().unwrap();
1018 let step = repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
1019 for sub_comp in &component_vec {
1020 let per_instance_start = constraints.len();
1021 generativity::make_guard!(guard);
1023 let sub_pin = sub_comp.as_pin_ref();
1024 let sub_borrow = sub_pin.borrow();
1025 let sub_instance_ref = unsafe { InstanceRef::from_pin_ref(sub_borrow, guard) };
1026 let expr_eval = |nr: &NamedReference| -> f32 {
1027 eval::load_property(sub_instance_ref, &nr.element(), nr.name())
1028 .unwrap()
1029 .try_into()
1030 .unwrap()
1031 };
1032
1033 for child_template in ci.iter() {
1037 match child_template {
1038 i_slint_compiler::layout::RowChildTemplate::Static(child_item) => {
1039 let mut layout_info = crate::eval_layout::get_layout_info(
1040 &child_item.element,
1041 sub_instance_ref,
1042 &sub_instance_ref.window_adapter(),
1043 orientation,
1044 );
1045 fill_layout_info_constraints(
1046 &mut layout_info,
1047 &child_item.constraints,
1048 orientation,
1049 &expr_eval,
1050 );
1051 constraints
1052 .push(core_layout::LayoutItemInfo { constraint: layout_info });
1053 }
1054 i_slint_compiler::layout::RowChildTemplate::Repeated {
1055 item: child_item,
1056 repeated_element,
1057 } => {
1058 let inner_instances =
1060 repeater_instances(sub_instance_ref, repeated_element);
1061 for inner_comp in &inner_instances {
1062 let inner_pin = inner_comp.as_pin_ref();
1063 let mut layout_info =
1064 inner_pin.layout_item_info(to_runtime(orientation), None);
1065 generativity::make_guard!(inner_guard);
1068 let inner_borrow = inner_pin.borrow();
1069 let inner_instance_ref = unsafe {
1070 InstanceRef::from_pin_ref(inner_borrow, inner_guard)
1071 };
1072 let inner_expr_eval = |nr: &NamedReference| -> f32 {
1073 eval::load_property(
1074 inner_instance_ref,
1075 &nr.element(),
1076 nr.name(),
1077 )
1078 .unwrap()
1079 .try_into()
1080 .unwrap()
1081 };
1082 fill_layout_info_constraints(
1083 &mut layout_info.constraint,
1084 &child_item.constraints,
1085 orientation,
1086 &inner_expr_eval,
1087 );
1088 constraints.push(layout_info);
1089 }
1090 }
1091 }
1092 }
1093 let pushed = constraints.len() - per_instance_start;
1096 for _ in pushed..step {
1097 constraints.push(core_layout::LayoutItemInfo::default());
1098 }
1099 }
1100 } else {
1101 constraints.extend(
1103 component_vec
1104 .iter()
1105 .map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
1106 );
1107 }
1108 repeater_idx += 1;
1109 } else {
1110 let cross_axis =
1111 cross_axis_size_for_cell(&layout_elem.item.element, orientation, cross_axis_size);
1112 let mut layout_info = get_layout_info_with_constraint(
1113 &layout_elem.item.element,
1114 component,
1115 &component.window_adapter(),
1116 orientation,
1117 cross_axis,
1118 );
1119 fill_layout_info_constraints(
1120 &mut layout_info,
1121 &layout_elem.item.constraints,
1122 orientation,
1123 &expr_eval,
1124 );
1125 constraints.push(core_layout::LayoutItemInfo { constraint: layout_info });
1126 }
1127 }
1128 constraints
1129}
1130
1131fn box_layout_data(
1133 box_layout: &i_slint_compiler::layout::BoxLayout,
1134 orientation: Orientation,
1135 component: InstanceRef,
1136 expr_eval: &impl Fn(&NamedReference) -> f32,
1137 mut repeater_indices: Option<&mut Vec<u32>>,
1138 cross_axis_size: Option<f32>,
1139 local_context: &mut EvalLocalContext,
1140 available_cross: Option<f32>,
1141) -> (Vec<core_layout::LayoutItemInfo>, i_slint_core::items::LayoutAlignment) {
1142 let window_adapter = component.window_adapter();
1143 let mut cells = Vec::with_capacity(box_layout.elems.len());
1144 for cell in &box_layout.elems {
1145 if cell.element.borrow().repeated.is_some() {
1146 let component_vec = repeater_instances(component, &cell.element);
1148 if let Some(ri) = repeater_indices.as_mut() {
1149 ri.push(cells.len() as _);
1150 ri.push(component_vec.len() as _);
1151 }
1152 cells.extend(
1153 component_vec
1154 .iter()
1155 .map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
1156 );
1157 } else {
1158 let cross_axis = cross_axis_size_for_cell(&cell.element, orientation, cross_axis_size);
1160 let mut layout_info = get_layout_info_with_constraint(
1161 &cell.element,
1162 component,
1163 &window_adapter,
1164 orientation,
1165 cross_axis,
1166 );
1167 clamp_wrapping_flex_cross_preferred(
1168 &mut layout_info,
1169 &cell.element,
1170 box_layout,
1171 orientation,
1172 component,
1173 &expr_eval,
1174 local_context,
1175 available_cross,
1176 );
1177 fill_layout_info_constraints(
1178 &mut layout_info,
1179 &cell.constraints,
1180 orientation,
1181 &expr_eval,
1182 );
1183 cells.push(core_layout::LayoutItemInfo { constraint: layout_info });
1184 }
1185 }
1186 let alignment = box_layout
1187 .geometry
1188 .alignment
1189 .as_ref()
1190 .map(|nr| {
1191 eval::load_property(component, &nr.element(), nr.name())
1192 .unwrap()
1193 .try_into()
1194 .unwrap_or_default()
1195 })
1196 .unwrap_or_default();
1197 (cells, alignment)
1198}
1199
1200#[allow(clippy::too_many_arguments)]
1211fn clamp_wrapping_flex_cross_preferred(
1212 layout_info: &mut core_layout::LayoutInfo,
1213 elem: &ElementRc,
1214 box_layout: &i_slint_compiler::layout::BoxLayout,
1215 orientation: Orientation,
1216 component: InstanceRef,
1217 expr_eval: &impl Fn(&NamedReference) -> f32,
1218 local_context: &mut EvalLocalContext,
1219 available_cross: Option<f32>,
1220) {
1221 let Some(available) = available_cross else { return };
1222 if orientation == box_layout.orientation {
1223 return;
1224 }
1225 let Some(fl) = i_slint_compiler::layout::FlexboxLayout::from_element(elem) else { return };
1226
1227 let direction = flexbox_layout_direction(&fl, local_context);
1229 let main_is_cross = matches!(
1230 (direction, orientation),
1231 (FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse, Orientation::Horizontal)
1232 | (
1233 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse,
1234 Orientation::Vertical
1235 )
1236 );
1237 if !main_is_cross {
1238 return;
1239 }
1240 let flex_wrap =
1241 fl.flex_wrap.as_ref().map_or(i_slint_core::items::FlexboxLayoutWrap::default(), |nr| {
1242 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
1243 });
1244 if matches!(flex_wrap, i_slint_core::items::FlexboxLayoutWrap::NoWrap) {
1245 return;
1246 }
1247
1248 let (cells_h, cells_v, _ri) =
1249 flexbox_layout_data(&fl, component, expr_eval, local_context, None, None);
1250 let (cells, padding, spacing) = match orientation {
1251 Orientation::Horizontal => {
1252 let (padding, spacing) =
1253 padding_and_spacing(&fl.geometry, Orientation::Horizontal, expr_eval);
1254 (cells_h, padding, spacing)
1255 }
1256 Orientation::Vertical => {
1257 let (padding, spacing) =
1258 padding_and_spacing(&fl.geometry, Orientation::Vertical, expr_eval);
1259 (cells_v, padding, spacing)
1260 }
1261 };
1262 let unwrapped = core_layout::flexbox_layout_unwrapped_main(
1263 Slice::from(cells.as_slice()),
1264 spacing,
1265 &padding,
1266 );
1267 layout_info.preferred = available.min(unwrapped);
1268}
1269
1270pub(crate) fn fill_layout_info_constraints(
1271 layout_info: &mut core_layout::LayoutInfo,
1272 constraints: &LayoutConstraints,
1273 orientation: Orientation,
1274 expr_eval: &impl Fn(&NamedReference) -> f32,
1275) {
1276 let is_percent =
1277 |nr: &NamedReference| Expression::PropertyReference(nr.clone()).ty() == Type::Percent;
1278
1279 match orientation {
1280 Orientation::Horizontal => {
1281 if let Some(e) = constraints.min_width.as_ref() {
1282 if !is_percent(e) {
1283 layout_info.min = expr_eval(e)
1284 } else {
1285 layout_info.min_percent = expr_eval(e)
1286 }
1287 }
1288 if let Some(e) = constraints.max_width.as_ref() {
1289 if !is_percent(e) {
1290 layout_info.max = expr_eval(e)
1291 } else {
1292 layout_info.max_percent = expr_eval(e)
1293 }
1294 }
1295 if let Some(e) = constraints.preferred_width.as_ref() {
1296 layout_info.preferred = expr_eval(e);
1297 }
1298 if let Some(e) = constraints.horizontal_stretch.as_ref() {
1299 layout_info.stretch = expr_eval(e);
1300 }
1301 }
1302 Orientation::Vertical => {
1303 if let Some(e) = constraints.min_height.as_ref() {
1304 if !is_percent(e) {
1305 layout_info.min = expr_eval(e)
1306 } else {
1307 layout_info.min_percent = expr_eval(e)
1308 }
1309 }
1310 if let Some(e) = constraints.max_height.as_ref() {
1311 if !is_percent(e) {
1312 layout_info.max = expr_eval(e)
1313 } else {
1314 layout_info.max_percent = expr_eval(e)
1315 }
1316 }
1317 if let Some(e) = constraints.preferred_height.as_ref() {
1318 layout_info.preferred = expr_eval(e);
1319 }
1320 if let Some(e) = constraints.vertical_stretch.as_ref() {
1321 layout_info.stretch = expr_eval(e);
1322 }
1323 }
1324 }
1325}
1326
1327pub(crate) fn get_layout_info(
1329 elem: &ElementRc,
1330 component: InstanceRef,
1331 window_adapter: &Rc<dyn WindowAdapter>,
1332 orientation: Orientation,
1333) -> core_layout::LayoutInfo {
1334 get_layout_info_with_constraint(elem, component, window_adapter, orientation, None)
1335}
1336
1337pub(crate) fn get_layout_info_with_constraint(
1338 elem: &ElementRc,
1339 component: InstanceRef,
1340 window_adapter: &Rc<dyn WindowAdapter>,
1341 orientation: Orientation,
1342 cross_axis_constraint: Option<f32>,
1343) -> core_layout::LayoutInfo {
1344 let parameterized_nr = if cross_axis_constraint.is_some() {
1350 match orientation {
1351 Orientation::Vertical => elem.borrow().inherited_layout_info_v_with_constraint(),
1352 Orientation::Horizontal => elem.borrow().inherited_layout_info_h_with_constraint(),
1353 }
1354 } else {
1355 None
1356 };
1357 if let Some(nr) = parameterized_nr {
1358 let arg = cross_axis_constraint.unwrap();
1359 let v = eval::call_function(
1360 &eval::ComponentInstance::InstanceRef(component),
1361 &nr.element(),
1362 nr.name(),
1363 vec![Value::Number(arg as f64)],
1364 )
1365 .expect("layoutinfo-{h,v}-with-constraint is a synthesized pure function");
1366 return v.try_into().unwrap();
1367 }
1368
1369 let elem = elem.borrow();
1370 if let Some(nr) = elem.layout_info_prop(orientation) {
1371 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
1372 } else {
1373 let item = &component
1374 .description
1375 .items
1376 .get(elem.id.as_str())
1377 .unwrap_or_else(|| panic!("Internal error: Item {} not found", elem.id));
1378 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
1379
1380 unsafe {
1381 item.item_from_item_tree(component.as_ptr()).as_ref().layout_info(
1382 to_runtime(orientation),
1383 cross_axis_constraint.unwrap_or(-1.),
1384 window_adapter,
1385 &ItemRc::new(vtable::VRc::into_dyn(item_comp), item.item_index()),
1386 )
1387 }
1388 }
1389}
1390
1391fn cross_axis_size_for_cell(
1398 elem: &ElementRc,
1399 orientation: Orientation,
1400 parent_cross_axis_size: Option<f32>,
1401) -> Option<f32> {
1402 let cross = parent_cross_axis_size?;
1403 let elem_b = elem.borrow();
1404 if orientation == Orientation::Horizontal {
1405 return elem_b.inherited_layout_info_h_with_constraint().is_some().then_some(cross);
1410 }
1411 if elem_b.layout_info_v_with_constraint.is_some() {
1412 return Some(cross);
1413 }
1414 if elem_b.layout_info_prop(Orientation::Vertical).is_none() {
1419 return Some(cross);
1420 }
1421 None
1422}