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 (cells, alignment) =
81 box_layout_data(box_layout, orientation, component, &expr_eval, None, cross_axis_size);
82 let (padding, spacing) = padding_and_spacing(&box_layout.geometry, orientation, &expr_eval);
83 if orientation == box_layout.orientation {
84 core_layout::box_layout_info(Slice::from(cells.as_slice()), spacing, &padding, alignment)
85 } else {
86 core_layout::box_layout_info_ortho(Slice::from(cells.as_slice()), &padding)
87 }
88 .into()
89}
90
91pub(crate) fn organize_grid_layout(
92 layout: &GridLayout,
93 local_context: &mut EvalLocalContext,
94) -> Value {
95 let repeater_steps = grid_repeater_steps(layout, local_context);
96 let cells = grid_layout_input_data(layout, local_context, &repeater_steps);
97 let repeater_indices = grid_repeater_indices(layout, local_context, &repeater_steps);
98 if let Some(buttons_roles) = &layout.dialog_button_roles {
99 let roles = buttons_roles
100 .iter()
101 .map(|r| DialogButtonRole::from_str(r).unwrap())
102 .collect::<Vec<_>>();
103 core_layout::organize_dialog_button_layout(
104 Slice::from_slice(cells.as_slice()),
105 Slice::from_slice(roles.as_slice()),
106 )
107 .into()
108 } else {
109 core_layout::organize_grid_layout(
110 Slice::from_slice(cells.as_slice()),
111 Slice::from_slice(repeater_indices.as_slice()),
112 Slice::from_slice(repeater_steps.as_slice()),
113 )
114 .into()
115 }
116}
117
118pub(crate) fn solve_grid_layout(
119 organized_data: &GridLayoutOrganizedData,
120 grid_layout: &GridLayout,
121 orientation: Orientation,
122 local_context: &mut EvalLocalContext,
123) -> Value {
124 let component = local_context.component_instance;
125 let expr_eval = |nr: &NamedReference| -> f32 {
126 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
127 };
128 let repeater_steps = grid_repeater_steps(grid_layout, local_context);
129 let repeater_indices = grid_repeater_indices(grid_layout, local_context, &repeater_steps);
130 let constraints =
131 grid_layout_constraints(grid_layout, orientation, local_context, &repeater_steps, None);
132
133 let (padding, spacing) = padding_and_spacing(&grid_layout.geometry, orientation, &expr_eval);
134 let size_ref = grid_layout.geometry.rect.size_reference(orientation);
135
136 let data = core_layout::GridLayoutData {
137 size: size_ref.map(expr_eval).unwrap_or(0.),
138 spacing,
139 padding,
140 organized_data: organized_data.clone(),
141 };
142
143 core_layout::solve_grid_layout(
144 &data,
145 Slice::from_slice(constraints.as_slice()),
146 to_runtime(orientation),
147 Slice::from_slice(repeater_indices.as_slice()),
148 Slice::from_slice(repeater_steps.as_slice()),
149 )
150 .into()
151}
152
153pub(crate) fn solve_box_layout(
154 box_layout: &BoxLayout,
155 orientation: Orientation,
156 local_context: &mut EvalLocalContext,
157) -> Value {
158 let component = local_context.component_instance;
159 let expr_eval = |nr: &NamedReference| -> f32 {
160 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
161 };
162
163 let mut repeated_indices = Vec::new();
164 let cross_axis_size = (orientation == box_layout.orientation
169 && orientation == Orientation::Horizontal
170 && box_layout
171 .elems
172 .iter()
173 .any(|c| c.element.borrow().inherited_layout_info_h_with_constraint().is_some()))
174 .then(|| {
175 let cross = orientation.orthogonal();
176 box_layout.geometry.rect.size_reference(cross).map(&expr_eval).map(|s| {
177 let (pad, _) = padding_and_spacing(&box_layout.geometry, cross, &expr_eval);
178 s - pad.begin - pad.end
179 })
180 })
181 .flatten();
182 let (cells, alignment) = box_layout_data(
183 box_layout,
184 orientation,
185 component,
186 &expr_eval,
187 Some(&mut repeated_indices),
188 cross_axis_size,
189 );
190 let (padding, spacing) = padding_and_spacing(&box_layout.geometry, orientation, &expr_eval);
191 let size = box_layout.geometry.rect.size_reference(orientation).map(&expr_eval).unwrap_or(0.);
192 if orientation == box_layout.orientation {
193 core_layout::solve_box_layout(
194 &core_layout::BoxLayoutData {
195 size,
196 spacing,
197 padding,
198 alignment,
199 cells: Slice::from(cells.as_slice()),
200 },
201 Slice::from(repeated_indices.as_slice()),
202 )
203 .into()
204 } else {
205 let cross_axis_alignment = box_layout
206 .cross_alignment
207 .as_ref()
208 .map(|nr| {
209 eval::load_property(component, &nr.element(), nr.name())
210 .unwrap()
211 .try_into()
212 .unwrap_or_default()
213 })
214 .unwrap_or_default();
215 core_layout::solve_box_layout_ortho(
216 &core_layout::BoxLayoutOrthoData {
217 size,
218 padding,
219 cross_axis_alignment,
220 cells: Slice::from(cells.as_slice()),
221 },
222 Slice::from(repeated_indices.as_slice()),
223 )
224 .into()
225 }
226}
227
228pub(crate) fn solve_flexbox_layout(
229 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
230 local_context: &mut EvalLocalContext,
231) -> Value {
232 let component = local_context.component_instance;
233 let expr_eval = |nr: &NamedReference| -> f32 {
234 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
235 };
236
237 let width_ref = &flexbox_layout.geometry.rect.width_reference;
238 let height_ref = &flexbox_layout.geometry.rect.height_reference;
239 let direction = flexbox_layout_direction(flexbox_layout, local_context);
240
241 let container_width_for_cells = match direction {
244 i_slint_core::items::FlexboxLayoutDirection::Column
245 | i_slint_core::items::FlexboxLayoutDirection::ColumnReverse => {
246 width_ref.as_ref().map(&expr_eval)
247 }
248 _ => None,
249 };
250
251 let (cells_h, cells_v, repeated_indices) = flexbox_layout_data(
252 flexbox_layout,
253 component,
254 &expr_eval,
255 local_context,
256 container_width_for_cells,
257 None,
258 );
259
260 let alignment = flexbox_layout
261 .geometry
262 .alignment
263 .as_ref()
264 .map_or(i_slint_core::items::LayoutAlignment::default(), |nr| {
265 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
266 });
267 let align_content = flexbox_layout
268 .align_content
269 .as_ref()
270 .map_or(i_slint_core::items::FlexboxLayoutAlignContent::default(), |nr| {
271 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
272 });
273 let cross_axis_alignment = flexbox_layout
274 .cross_axis_alignment
275 .as_ref()
276 .map_or(i_slint_core::items::CrossAxisAlignment::default(), |nr| {
277 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
278 });
279 let flex_wrap = flexbox_layout
280 .flex_wrap
281 .as_ref()
282 .map_or(i_slint_core::items::FlexboxLayoutWrap::default(), |nr| {
283 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
284 });
285
286 let (padding_h, spacing_h) =
287 padding_and_spacing(&flexbox_layout.geometry, Orientation::Horizontal, &expr_eval);
288 let (padding_v, spacing_v) =
289 padding_and_spacing(&flexbox_layout.geometry, Orientation::Vertical, &expr_eval);
290
291 let data = core_layout::FlexboxLayoutData {
292 width: width_ref.as_ref().map(&expr_eval).unwrap_or(0.),
293 height: height_ref.as_ref().map(&expr_eval).unwrap_or(0.),
294 spacing_h,
295 spacing_v,
296 padding_h,
297 padding_v,
298 alignment,
299 direction,
300 align_content,
301 cross_axis_alignment,
302 flex_wrap,
303 cells_h: Slice::from(cells_h.as_slice()),
304 cells_v: Slice::from(cells_v.as_slice()),
305 };
306 let ri = Slice::from(repeated_indices.as_slice());
307
308 let window_adapter = component.window_adapter();
309
310 struct ChildElem {
320 elem: ElementRc,
321 has_constrained_layoutinfo_v: bool,
322 has_constrained_layoutinfo_h: bool,
323 has_aggregated_info: bool,
330 }
331 let mut child_elems: Vec<Option<ChildElem>> = Vec::new();
332 for layout_elem in &flexbox_layout.elems {
333 if layout_elem.item.element.borrow().repeated.is_some() {
334 let component_vec = repeater_instances(component, &layout_elem.item.element);
335 for _ in 0..component_vec.len() {
336 child_elems.push(None);
337 }
338 } else {
339 let elem_b = layout_elem.item.element.borrow();
340 let has_constrained_layoutinfo_v =
341 elem_b.inherited_layout_info_v_with_constraint().is_some();
342 let has_constrained_layoutinfo_h =
343 elem_b.inherited_layout_info_h_with_constraint().is_some();
344 let has_aggregated_info = elem_b.layout_info_prop.is_some();
345 drop(elem_b);
346 child_elems.push(Some(ChildElem {
347 elem: layout_elem.item.element.clone(),
348 has_constrained_layoutinfo_v,
349 has_constrained_layoutinfo_h,
350 has_aggregated_info,
351 }));
352 }
353 }
354
355 let mut measure = |child_index: usize,
356 known_w: Option<f32>,
357 known_h: Option<f32>|
358 -> (f32, f32) {
359 let default_w = cells_h.get(child_index).map_or(0., |c| c.constraint.preferred_bounded());
360 let default_h = cells_v.get(child_index).map_or(0., |c| c.constraint.preferred_bounded());
361 let w = known_w.unwrap_or(default_w);
362 let h = known_h.unwrap_or(default_h);
363
364 let ce = match child_elems.get(child_index) {
365 Some(Some(c)) => c,
366 _ => return (w, h),
367 };
368
369 let use_property_lookup = ce.has_aggregated_info
375 || ce.has_constrained_layoutinfo_v
376 || ce.has_constrained_layoutinfo_h;
377
378 if known_w.is_some() && known_h.is_none() {
379 if use_property_lookup {
380 let v_info = get_layout_info_with_constraint(
381 &ce.elem,
382 component,
383 &window_adapter,
384 Orientation::Vertical,
385 ce.has_constrained_layoutinfo_v.then_some(w),
386 );
387 return (w, v_info.preferred_bounded());
388 }
389 let elem_id = ce.elem.borrow().id.clone();
392 if let Some(item_within) = component.description.items.get(elem_id.as_str()) {
393 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
394 let item_rc =
395 ItemRc::new(vtable::VRc::into_dyn(item_comp), item_within.item_index());
396 let item = unsafe { item_within.item_from_item_tree(component.as_ptr()) };
397 let v_info = item.as_ref().layout_info(
398 to_runtime(Orientation::Vertical),
399 w,
400 &window_adapter,
401 &item_rc,
402 );
403 return (w, v_info.preferred_bounded());
404 }
405 return (w, h);
406 }
407 if known_h.is_some() && known_w.is_none() {
408 if use_property_lookup {
409 let h_info = get_layout_info_with_constraint(
410 &ce.elem,
411 component,
412 &window_adapter,
413 Orientation::Horizontal,
414 ce.has_constrained_layoutinfo_h.then_some(h),
415 );
416 return (h_info.preferred_bounded(), h);
417 }
418 let elem_id = ce.elem.borrow().id.clone();
419 if let Some(item_within) = component.description.items.get(elem_id.as_str()) {
420 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
421 let item_rc =
422 ItemRc::new(vtable::VRc::into_dyn(item_comp), item_within.item_index());
423 let item = unsafe { item_within.item_from_item_tree(component.as_ptr()) };
424 let h_info = item.as_ref().layout_info(
425 to_runtime(Orientation::Horizontal),
426 h,
427 &window_adapter,
428 &item_rc,
429 );
430 return (h_info.preferred_bounded(), h);
431 }
432 return (w, h);
433 }
434 (w, h)
435 };
436
437 core_layout::solve_flexbox_layout_with_measure(&data, ri, Some(&mut measure)).into()
438}
439
440fn flexbox_layout_direction(
441 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
442 local_context: &EvalLocalContext,
443) -> FlexboxLayoutDirection {
444 flexbox_layout
445 .direction
446 .as_ref()
447 .and_then(|nr| {
448 let value =
449 eval::load_property(local_context.component_instance, &nr.element(), nr.name())
450 .ok()?;
451 if let Value::EnumerationValue(_, variant) = &value {
452 match variant.as_str() {
453 "row" => Some(FlexboxLayoutDirection::Row),
454 "row-reverse" => Some(FlexboxLayoutDirection::RowReverse),
455 "column" => Some(FlexboxLayoutDirection::Column),
456 "column-reverse" => Some(FlexboxLayoutDirection::ColumnReverse),
457 _ => None,
458 }
459 } else {
460 None
461 }
462 })
463 .unwrap_or(FlexboxLayoutDirection::Row)
464}
465
466pub(crate) fn compute_flexbox_layout_info(
467 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
468 orientation: Orientation,
469 local_context: &mut EvalLocalContext,
470 cross_axis_size: Option<f32>,
471) -> Value {
472 let component = local_context.component_instance;
473 let expr_eval = |nr: &NamedReference| -> f32 {
474 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
475 };
476
477 let (width_override, height_override) = match orientation {
482 Orientation::Vertical => (cross_axis_size, None),
483 Orientation::Horizontal => (None, cross_axis_size),
484 };
485 let (cells_h, cells_v, _repeated_indices) = flexbox_layout_data(
486 flexbox_layout,
487 component,
488 &expr_eval,
489 local_context,
490 width_override,
491 height_override,
492 );
493
494 let direction = flexbox_layout_direction(flexbox_layout, local_context);
496
497 let is_main_axis = matches!(
499 (direction, orientation),
500 (FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse, Orientation::Horizontal)
501 | (
502 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse,
503 Orientation::Vertical
504 )
505 );
506
507 let (padding_h, spacing_h) =
508 padding_and_spacing(&flexbox_layout.geometry, Orientation::Horizontal, &expr_eval);
509 let (padding_v, spacing_v) =
510 padding_and_spacing(&flexbox_layout.geometry, Orientation::Vertical, &expr_eval);
511
512 let flex_wrap = flexbox_layout
513 .flex_wrap
514 .as_ref()
515 .map_or(i_slint_core::items::FlexboxLayoutWrap::default(), |nr| {
516 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
517 });
518
519 if is_main_axis {
520 let (cells, spacing, padding) = match orientation {
521 Orientation::Horizontal => (&cells_h, spacing_h, &padding_h),
522 Orientation::Vertical => (&cells_v, spacing_v, &padding_v),
523 };
524 core_layout::flexbox_layout_info_main_axis(
525 Slice::from(cells.as_slice()),
526 spacing,
527 padding,
528 flex_wrap,
529 )
530 .into()
531 } else {
532 let constraint_size = cross_axis_size.unwrap_or_else(|| match orientation {
536 Orientation::Horizontal => {
537 let height_ref = &flexbox_layout.geometry.rect.height_reference;
538 height_ref.as_ref().map(&expr_eval).unwrap_or(0.)
539 }
540 Orientation::Vertical => {
541 let width_ref = &flexbox_layout.geometry.rect.width_reference;
542 width_ref.as_ref().map(&expr_eval).unwrap_or(0.)
543 }
544 });
545 core_layout::flexbox_layout_info_cross_axis(
546 Slice::from(cells_h.as_slice()),
547 Slice::from(cells_v.as_slice()),
548 spacing_h,
549 spacing_v,
550 &padding_h,
551 &padding_v,
552 direction,
553 flex_wrap,
554 constraint_size,
555 )
556 .into()
557 }
558}
559
560fn flexbox_layout_data(
561 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
562 component: InstanceRef,
563 expr_eval: &impl Fn(&NamedReference) -> f32,
564 _local_context: &mut EvalLocalContext,
565 width_override: Option<f32>,
566 height_override: Option<f32>,
567) -> (Vec<core_layout::FlexboxLayoutItemInfo>, Vec<core_layout::FlexboxLayoutItemInfo>, Vec<u32>) {
568 let window_adapter = component.window_adapter();
569 let mut cells_h = Vec::with_capacity(flexbox_layout.elems.len());
570 let mut cells_v = Vec::with_capacity(flexbox_layout.elems.len());
571 let mut repeated_indices = Vec::new();
572
573 struct ChildInfo {
576 flex_grow: f32,
577 flex_shrink: f32,
578 flex_basis: f32,
579 flex_align_self: i_slint_core::items::FlexboxLayoutAlignSelf,
580 flex_order: i32,
581 }
582 let mut static_children: Vec<Option<ChildInfo>> = Vec::new(); for layout_elem in &flexbox_layout.elems {
585 if layout_elem.item.element.borrow().repeated.is_some() {
586 let component_vec = repeater_instances(component, &layout_elem.item.element);
587 repeated_indices.push(cells_h.len() as u32);
588 repeated_indices.push(component_vec.len() as u32);
589 cells_h.extend(component_vec.iter().map(|x| {
590 x.as_pin_ref().flexbox_layout_item_info(to_runtime(Orientation::Horizontal), None)
591 }));
592 cells_v.extend(component_vec.iter().map(|x| {
593 x.as_pin_ref().flexbox_layout_item_info(to_runtime(Orientation::Vertical), None)
594 }));
595 for _ in 0..component_vec.len() {
596 static_children.push(None);
597 }
598 } else {
599 let h_constraint = layout_elem
605 .item
606 .element
607 .borrow()
608 .inherited_layout_info_h_with_constraint()
609 .is_some()
610 .then(|| height_override.unwrap_or(f32::MAX));
611 let mut layout_info_h = get_layout_info_with_constraint(
612 &layout_elem.item.element,
613 component,
614 &window_adapter,
615 Orientation::Horizontal,
616 h_constraint,
617 );
618 fill_layout_info_constraints(
619 &mut layout_info_h,
620 &layout_elem.item.constraints,
621 Orientation::Horizontal,
622 expr_eval,
623 );
624 let flex_grow = layout_elem.flex_grow.as_ref().map(&expr_eval).unwrap_or(0.0);
628 let flex_shrink = layout_elem.flex_shrink.as_ref().map(&expr_eval).unwrap_or(1.0);
629 let flex_basis = layout_elem.flex_basis.as_ref().map(&expr_eval).unwrap_or(-1.0);
630 let align_self = layout_elem
631 .align_self
632 .as_ref()
633 .map(|nr| {
634 eval::load_property(component, &nr.element(), nr.name())
635 .unwrap()
636 .try_into()
637 .unwrap()
638 })
639 .unwrap_or(i_slint_core::items::FlexboxLayoutAlignSelf::default());
640 let order = layout_elem.order.as_ref().map(expr_eval).unwrap_or(0.0) as i32;
641 cells_h.push(core_layout::FlexboxLayoutItemInfo {
642 constraint: layout_info_h,
643 flex_grow,
644 flex_shrink,
645 flex_basis,
646 flex_align_self: align_self,
647 flex_order: order,
648 });
649 cells_v.push(core_layout::FlexboxLayoutItemInfo::default());
651 static_children.push(Some(ChildInfo {
652 flex_grow,
653 flex_shrink,
654 flex_basis,
655 flex_align_self: align_self,
656 flex_order: order,
657 }));
658 }
659 }
660
661 let mut cell_idx = 0usize;
665 for layout_elem in &flexbox_layout.elems {
666 if layout_elem.item.element.borrow().repeated.is_some() {
667 let component_vec = repeater_instances(component, &layout_elem.item.element);
668 cell_idx += component_vec.len();
669 } else {
671 let width_constraint =
672 width_override.unwrap_or_else(|| cells_h[cell_idx].constraint.preferred_bounded());
673 let mut layout_info_v = get_layout_info_with_constraint(
674 &layout_elem.item.element,
675 component,
676 &window_adapter,
677 Orientation::Vertical,
678 Some(width_constraint),
679 );
680 fill_layout_info_constraints(
681 &mut layout_info_v,
682 &layout_elem.item.constraints,
683 Orientation::Vertical,
684 expr_eval,
685 );
686 if let Some(info) = &static_children[cell_idx] {
687 cells_v[cell_idx] = core_layout::FlexboxLayoutItemInfo {
688 constraint: layout_info_v,
689 flex_grow: info.flex_grow,
690 flex_shrink: info.flex_shrink,
691 flex_basis: info.flex_basis,
692 flex_align_self: info.flex_align_self,
693 flex_order: info.flex_order,
694 };
695 }
696 cell_idx += 1;
697 }
698 }
699
700 (cells_h, cells_v, repeated_indices)
701}
702
703fn padding_and_spacing(
705 layout_geometry: &LayoutGeometry,
706 orientation: Orientation,
707 expr_eval: &impl Fn(&NamedReference) -> f32,
708) -> (core_layout::Padding, f32) {
709 let spacing = layout_geometry.spacing.orientation(orientation).map_or(0., expr_eval);
710 let (begin, end) = layout_geometry.padding.begin_end(orientation);
711 let padding =
712 core_layout::Padding { begin: begin.map_or(0., expr_eval), end: end.map_or(0., expr_eval) };
713 (padding, spacing)
714}
715
716fn repeater_instances(
717 component: InstanceRef,
718 elem: &ElementRc,
719) -> Vec<crate::dynamic_item_tree::DynamicComponentVRc> {
720 generativity::make_guard!(guard);
721 let rep =
722 crate::dynamic_item_tree::get_repeater_by_name(component, elem.borrow().id.as_str(), guard);
723 rep.0.as_ref().track_instance_changes();
724 rep.0.as_ref().instances_vec()
725}
726
727fn grid_layout_input_data(
728 grid_layout: &i_slint_compiler::layout::GridLayout,
729 ctx: &EvalLocalContext,
730 repeater_steps: &[u32],
731) -> Vec<GridLayoutInputData> {
732 let component = ctx.component_instance;
733 let mut result = Vec::with_capacity(grid_layout.elems.len());
734 let mut after_repeater_in_same_row = false;
735 let mut new_row = true;
736 let mut repeater_idx = 0usize;
737 for elem in grid_layout.elems.iter() {
738 let eval_or_default = |expr: &RowColExpr, component: InstanceRef| match expr {
739 RowColExpr::Literal(value) => *value as f32,
740 RowColExpr::Auto => i_slint_common::ROW_COL_AUTO,
741 RowColExpr::Named(nr) => {
742 eval::load_property(component, &nr.element(), nr.name())
744 .unwrap()
745 .try_into()
746 .unwrap()
747 }
748 };
749
750 let cell_new_row = elem.cell.borrow().new_row;
751 if cell_new_row {
752 after_repeater_in_same_row = false;
753 }
754 if elem.item.element.borrow().repeated.is_some() {
755 let component_vec = repeater_instances(component, &elem.item.element);
756 new_row = cell_new_row;
757 for erased_sub_comp in &component_vec {
758 generativity::make_guard!(guard);
760 let sub_comp = erased_sub_comp.as_pin_ref();
761 let sub_instance_ref =
762 unsafe { InstanceRef::from_pin_ref(sub_comp.borrow(), guard) };
763
764 if let Some(children) = elem.cell.borrow().child_items.as_ref() {
765 new_row = true;
767 let start_count = result.len();
768
769 for child_template in children {
775 match child_template {
776 i_slint_compiler::layout::RowChildTemplate::Static(child_item) => {
777 let (row_val, col_val, rowspan_val, colspan_val) = {
778 let element_ref = child_item.element.borrow();
779 let child_cell =
780 element_ref.grid_layout_cell.as_ref().unwrap().borrow();
781 (
782 eval_or_default(&child_cell.row_expr, sub_instance_ref),
783 eval_or_default(&child_cell.col_expr, sub_instance_ref),
784 eval_or_default(&child_cell.rowspan_expr, sub_instance_ref),
785 eval_or_default(&child_cell.colspan_expr, sub_instance_ref),
786 )
787 };
788 result.push(GridLayoutInputData {
789 new_row,
790 col: col_val,
791 row: row_val,
792 colspan: colspan_val,
793 rowspan: rowspan_val,
794 });
795 new_row = false;
796 }
797 i_slint_compiler::layout::RowChildTemplate::Repeated {
798 repeated_element,
799 ..
800 } => {
801 let inner_root = repeated_element
804 .borrow()
805 .base_type
806 .as_component()
807 .root_element
808 .clone();
809 let (rowspan_expr, colspan_expr) = {
810 let element_ref = inner_root.borrow();
811 let child_cell =
812 element_ref.grid_layout_cell.as_ref().unwrap().borrow();
813 (
814 child_cell.rowspan_expr.clone(),
815 child_cell.colspan_expr.clone(),
816 )
817 };
818 let inner_instances =
819 repeater_instances(sub_instance_ref, repeated_element);
820 for (i, erased_inner) in inner_instances.iter().enumerate() {
821 generativity::make_guard!(inner_guard);
822 let inner_comp = erased_inner.as_pin_ref();
823 let inner_instance_ref = unsafe {
824 InstanceRef::from_pin_ref(inner_comp.borrow(), inner_guard)
825 };
826 result.push(GridLayoutInputData {
827 new_row: i == 0 && new_row,
828 rowspan: eval_or_default(&rowspan_expr, inner_instance_ref),
829 colspan: eval_or_default(&colspan_expr, inner_instance_ref),
830 ..Default::default()
831 });
832 }
833 if !inner_instances.is_empty() {
834 new_row = false;
835 }
836 }
837 }
838 }
839 let cells_pushed = result.len() - start_count;
841 let expected_step =
842 repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
843 for _ in cells_pushed..expected_step {
844 result.push(GridLayoutInputData::default());
845 }
846 } else {
847 let cell = elem.cell.borrow();
849 let row = eval_or_default(&cell.row_expr, sub_instance_ref);
850 let col = eval_or_default(&cell.col_expr, sub_instance_ref);
851 let rowspan = eval_or_default(&cell.rowspan_expr, sub_instance_ref);
852 let colspan = eval_or_default(&cell.colspan_expr, sub_instance_ref);
853 result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
854 new_row = false;
855 }
856 }
857 repeater_idx += 1;
858 after_repeater_in_same_row = true;
859 } else {
860 let new_row =
861 if cell_new_row || !after_repeater_in_same_row { cell_new_row } else { new_row };
862 let row = eval_or_default(&elem.cell.borrow().row_expr, component);
863 let col = eval_or_default(&elem.cell.borrow().col_expr, component);
864 let rowspan = eval_or_default(&elem.cell.borrow().rowspan_expr, component);
865 let colspan = eval_or_default(&elem.cell.borrow().colspan_expr, component);
866 result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
867 }
868 }
869 result
870}
871
872fn row_runtime_child_count(
876 child_items: &[i_slint_compiler::layout::RowChildTemplate],
877 sub_instance_ref: InstanceRef,
878) -> usize {
879 let mut count = 0;
880 for child in child_items {
881 if let Some(repeated_element) = child.repeated_element() {
882 count += repeater_instances(sub_instance_ref, repeated_element).len();
883 } else {
884 count += 1;
885 }
886 }
887 count
888}
889
890fn grid_repeater_indices(
891 grid_layout: &i_slint_compiler::layout::GridLayout,
892 ctx: &mut EvalLocalContext,
893 repeater_steps: &[u32],
894) -> Vec<u32> {
895 let component = ctx.component_instance;
896 let mut repeater_indices = Vec::new();
897 let mut num_cells = 0;
898 let mut step_idx = 0;
899 for elem in grid_layout.elems.iter() {
900 if elem.item.element.borrow().repeated.is_some() {
901 let component_vec = repeater_instances(component, &elem.item.element);
902 repeater_indices.push(num_cells as _);
903 repeater_indices.push(component_vec.len() as _);
904 let item_count = repeater_steps[step_idx] as usize;
905 num_cells += component_vec.len() * item_count;
906 step_idx += 1;
907 } else {
908 num_cells += 1;
909 }
910 }
911 repeater_indices
912}
913
914fn grid_repeater_steps(
915 grid_layout: &i_slint_compiler::layout::GridLayout,
916 ctx: &mut EvalLocalContext,
917) -> Vec<u32> {
918 let component = ctx.component_instance;
919 let mut repeater_steps = Vec::new();
920 for elem in grid_layout.elems.iter() {
921 if elem.item.element.borrow().repeated.is_some() {
922 let item_count = match &elem.cell.borrow().child_items {
923 Some(ci)
924 if ci.iter().any(i_slint_compiler::layout::RowChildTemplate::is_repeated) =>
925 {
926 let component_vec = repeater_instances(component, &elem.item.element);
928 component_vec
929 .iter()
930 .map(|sub| {
931 generativity::make_guard!(guard);
932 let sub_pin = sub.as_pin_ref();
933 let sub_ref =
934 unsafe { InstanceRef::from_pin_ref(sub_pin.borrow(), guard) };
935 row_runtime_child_count(ci, sub_ref)
936 })
937 .max()
938 .unwrap_or(0)
939 }
940 Some(ci) => ci.len(),
941 None => 1,
942 };
943 repeater_steps.push(item_count as u32);
944 }
945 }
946 repeater_steps
947}
948
949fn grid_layout_constraints(
950 grid_layout: &i_slint_compiler::layout::GridLayout,
951 orientation: Orientation,
952 ctx: &mut EvalLocalContext,
953 repeater_steps: &[u32],
954 cross_axis_size: Option<f32>,
955) -> Vec<core_layout::LayoutItemInfo> {
956 let component = ctx.component_instance;
957 let expr_eval = |nr: &NamedReference| -> f32 {
958 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
959 };
960 let mut constraints = Vec::with_capacity(grid_layout.elems.len());
961
962 let mut repeater_idx = 0usize;
963 for layout_elem in grid_layout.elems.iter() {
964 if layout_elem.item.element.borrow().repeated.is_some() {
965 let component_vec = repeater_instances(component, &layout_elem.item.element);
966 let child_items = layout_elem.cell.borrow().child_items.clone();
967 let has_children = child_items.is_some();
968 if has_children {
969 let ci = child_items.as_ref().unwrap();
971 let step = repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
972 for sub_comp in &component_vec {
973 let per_instance_start = constraints.len();
974 generativity::make_guard!(guard);
976 let sub_pin = sub_comp.as_pin_ref();
977 let sub_borrow = sub_pin.borrow();
978 let sub_instance_ref = unsafe { InstanceRef::from_pin_ref(sub_borrow, guard) };
979 let expr_eval = |nr: &NamedReference| -> f32 {
980 eval::load_property(sub_instance_ref, &nr.element(), nr.name())
981 .unwrap()
982 .try_into()
983 .unwrap()
984 };
985
986 for child_template in ci.iter() {
990 match child_template {
991 i_slint_compiler::layout::RowChildTemplate::Static(child_item) => {
992 let mut layout_info = crate::eval_layout::get_layout_info(
993 &child_item.element,
994 sub_instance_ref,
995 &sub_instance_ref.window_adapter(),
996 orientation,
997 );
998 fill_layout_info_constraints(
999 &mut layout_info,
1000 &child_item.constraints,
1001 orientation,
1002 &expr_eval,
1003 );
1004 constraints
1005 .push(core_layout::LayoutItemInfo { constraint: layout_info });
1006 }
1007 i_slint_compiler::layout::RowChildTemplate::Repeated {
1008 item: child_item,
1009 repeated_element,
1010 } => {
1011 let inner_instances =
1013 repeater_instances(sub_instance_ref, repeated_element);
1014 for inner_comp in &inner_instances {
1015 let inner_pin = inner_comp.as_pin_ref();
1016 let mut layout_info =
1017 inner_pin.layout_item_info(to_runtime(orientation), None);
1018 generativity::make_guard!(inner_guard);
1021 let inner_borrow = inner_pin.borrow();
1022 let inner_instance_ref = unsafe {
1023 InstanceRef::from_pin_ref(inner_borrow, inner_guard)
1024 };
1025 let inner_expr_eval = |nr: &NamedReference| -> f32 {
1026 eval::load_property(
1027 inner_instance_ref,
1028 &nr.element(),
1029 nr.name(),
1030 )
1031 .unwrap()
1032 .try_into()
1033 .unwrap()
1034 };
1035 fill_layout_info_constraints(
1036 &mut layout_info.constraint,
1037 &child_item.constraints,
1038 orientation,
1039 &inner_expr_eval,
1040 );
1041 constraints.push(layout_info);
1042 }
1043 }
1044 }
1045 }
1046 let pushed = constraints.len() - per_instance_start;
1049 for _ in pushed..step {
1050 constraints.push(core_layout::LayoutItemInfo::default());
1051 }
1052 }
1053 } else {
1054 constraints.extend(
1056 component_vec
1057 .iter()
1058 .map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
1059 );
1060 }
1061 repeater_idx += 1;
1062 } else {
1063 let cross_axis =
1064 cross_axis_size_for_cell(&layout_elem.item.element, orientation, cross_axis_size);
1065 let mut layout_info = get_layout_info_with_constraint(
1066 &layout_elem.item.element,
1067 component,
1068 &component.window_adapter(),
1069 orientation,
1070 cross_axis,
1071 );
1072 fill_layout_info_constraints(
1073 &mut layout_info,
1074 &layout_elem.item.constraints,
1075 orientation,
1076 &expr_eval,
1077 );
1078 constraints.push(core_layout::LayoutItemInfo { constraint: layout_info });
1079 }
1080 }
1081 constraints
1082}
1083
1084fn box_layout_data(
1086 box_layout: &i_slint_compiler::layout::BoxLayout,
1087 orientation: Orientation,
1088 component: InstanceRef,
1089 expr_eval: &impl Fn(&NamedReference) -> f32,
1090 mut repeater_indices: Option<&mut Vec<u32>>,
1091 cross_axis_size: Option<f32>,
1092) -> (Vec<core_layout::LayoutItemInfo>, i_slint_core::items::LayoutAlignment) {
1093 let window_adapter = component.window_adapter();
1094 let mut cells = Vec::with_capacity(box_layout.elems.len());
1095 for cell in &box_layout.elems {
1096 if cell.element.borrow().repeated.is_some() {
1097 let component_vec = repeater_instances(component, &cell.element);
1099 if let Some(ri) = repeater_indices.as_mut() {
1100 ri.push(cells.len() as _);
1101 ri.push(component_vec.len() as _);
1102 }
1103 cells.extend(
1104 component_vec
1105 .iter()
1106 .map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
1107 );
1108 } else {
1109 let cross_axis = cross_axis_size_for_cell(&cell.element, orientation, cross_axis_size);
1111 let mut layout_info = get_layout_info_with_constraint(
1112 &cell.element,
1113 component,
1114 &window_adapter,
1115 orientation,
1116 cross_axis,
1117 );
1118 fill_layout_info_constraints(
1119 &mut layout_info,
1120 &cell.constraints,
1121 orientation,
1122 &expr_eval,
1123 );
1124 cells.push(core_layout::LayoutItemInfo { constraint: layout_info });
1125 }
1126 }
1127 let alignment = box_layout
1128 .geometry
1129 .alignment
1130 .as_ref()
1131 .map(|nr| {
1132 eval::load_property(component, &nr.element(), nr.name())
1133 .unwrap()
1134 .try_into()
1135 .unwrap_or_default()
1136 })
1137 .unwrap_or_default();
1138 (cells, alignment)
1139}
1140
1141pub(crate) fn fill_layout_info_constraints(
1142 layout_info: &mut core_layout::LayoutInfo,
1143 constraints: &LayoutConstraints,
1144 orientation: Orientation,
1145 expr_eval: &impl Fn(&NamedReference) -> f32,
1146) {
1147 let is_percent =
1148 |nr: &NamedReference| Expression::PropertyReference(nr.clone()).ty() == Type::Percent;
1149
1150 match orientation {
1151 Orientation::Horizontal => {
1152 if let Some(e) = constraints.min_width.as_ref() {
1153 if !is_percent(e) {
1154 layout_info.min = expr_eval(e)
1155 } else {
1156 layout_info.min_percent = expr_eval(e)
1157 }
1158 }
1159 if let Some(e) = constraints.max_width.as_ref() {
1160 if !is_percent(e) {
1161 layout_info.max = expr_eval(e)
1162 } else {
1163 layout_info.max_percent = expr_eval(e)
1164 }
1165 }
1166 if let Some(e) = constraints.preferred_width.as_ref() {
1167 layout_info.preferred = expr_eval(e);
1168 }
1169 if let Some(e) = constraints.horizontal_stretch.as_ref() {
1170 layout_info.stretch = expr_eval(e);
1171 }
1172 }
1173 Orientation::Vertical => {
1174 if let Some(e) = constraints.min_height.as_ref() {
1175 if !is_percent(e) {
1176 layout_info.min = expr_eval(e)
1177 } else {
1178 layout_info.min_percent = expr_eval(e)
1179 }
1180 }
1181 if let Some(e) = constraints.max_height.as_ref() {
1182 if !is_percent(e) {
1183 layout_info.max = expr_eval(e)
1184 } else {
1185 layout_info.max_percent = expr_eval(e)
1186 }
1187 }
1188 if let Some(e) = constraints.preferred_height.as_ref() {
1189 layout_info.preferred = expr_eval(e);
1190 }
1191 if let Some(e) = constraints.vertical_stretch.as_ref() {
1192 layout_info.stretch = expr_eval(e);
1193 }
1194 }
1195 }
1196}
1197
1198pub(crate) fn get_layout_info(
1200 elem: &ElementRc,
1201 component: InstanceRef,
1202 window_adapter: &Rc<dyn WindowAdapter>,
1203 orientation: Orientation,
1204) -> core_layout::LayoutInfo {
1205 get_layout_info_with_constraint(elem, component, window_adapter, orientation, None)
1206}
1207
1208pub(crate) fn get_layout_info_with_constraint(
1209 elem: &ElementRc,
1210 component: InstanceRef,
1211 window_adapter: &Rc<dyn WindowAdapter>,
1212 orientation: Orientation,
1213 cross_axis_constraint: Option<f32>,
1214) -> core_layout::LayoutInfo {
1215 let parameterized_nr = if cross_axis_constraint.is_some() {
1221 match orientation {
1222 Orientation::Vertical => elem.borrow().inherited_layout_info_v_with_constraint(),
1223 Orientation::Horizontal => elem.borrow().inherited_layout_info_h_with_constraint(),
1224 }
1225 } else {
1226 None
1227 };
1228 if let Some(nr) = parameterized_nr {
1229 let arg = cross_axis_constraint.unwrap();
1230 let v = eval::call_function(
1231 &eval::ComponentInstance::InstanceRef(component),
1232 &nr.element(),
1233 nr.name(),
1234 vec![Value::Number(arg as f64)],
1235 )
1236 .expect("layoutinfo-{h,v}-with-constraint is a synthesized pure function");
1237 return v.try_into().unwrap();
1238 }
1239
1240 let elem = elem.borrow();
1241 if let Some(nr) = elem.layout_info_prop(orientation) {
1242 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
1243 } else {
1244 let item = &component
1245 .description
1246 .items
1247 .get(elem.id.as_str())
1248 .unwrap_or_else(|| panic!("Internal error: Item {} not found", elem.id));
1249 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
1250
1251 unsafe {
1252 item.item_from_item_tree(component.as_ptr()).as_ref().layout_info(
1253 to_runtime(orientation),
1254 cross_axis_constraint.unwrap_or(-1.),
1255 window_adapter,
1256 &ItemRc::new(vtable::VRc::into_dyn(item_comp), item.item_index()),
1257 )
1258 }
1259 }
1260}
1261
1262fn cross_axis_size_for_cell(
1269 elem: &ElementRc,
1270 orientation: Orientation,
1271 parent_cross_axis_size: Option<f32>,
1272) -> Option<f32> {
1273 let cross = parent_cross_axis_size?;
1274 let elem_b = elem.borrow();
1275 if orientation == Orientation::Horizontal {
1276 return elem_b.inherited_layout_info_h_with_constraint().is_some().then_some(cross);
1281 }
1282 if elem_b.layout_info_v_with_constraint.is_some() {
1283 return Some(cross);
1284 }
1285 if elem_b.layout_info_prop(Orientation::Vertical).is_none() {
1290 return Some(cross);
1291 }
1292 None
1293}