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_instances =
802 repeater_instances(sub_instance_ref, repeated_element);
803 for i in 0..inner_instances.len() {
804 result.push(GridLayoutInputData {
805 new_row: i == 0 && new_row,
806 ..Default::default()
807 });
808 }
809 if !inner_instances.is_empty() {
810 new_row = false;
811 }
812 }
813 }
814 }
815 let cells_pushed = result.len() - start_count;
817 let expected_step =
818 repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
819 for _ in cells_pushed..expected_step {
820 result.push(GridLayoutInputData::default());
821 }
822 } else {
823 let cell = elem.cell.borrow();
825 let row = eval_or_default(&cell.row_expr, sub_instance_ref);
826 let col = eval_or_default(&cell.col_expr, sub_instance_ref);
827 let rowspan = eval_or_default(&cell.rowspan_expr, sub_instance_ref);
828 let colspan = eval_or_default(&cell.colspan_expr, sub_instance_ref);
829 result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
830 new_row = false;
831 }
832 }
833 repeater_idx += 1;
834 after_repeater_in_same_row = true;
835 } else {
836 let new_row =
837 if cell_new_row || !after_repeater_in_same_row { cell_new_row } else { new_row };
838 let row = eval_or_default(&elem.cell.borrow().row_expr, component);
839 let col = eval_or_default(&elem.cell.borrow().col_expr, component);
840 let rowspan = eval_or_default(&elem.cell.borrow().rowspan_expr, component);
841 let colspan = eval_or_default(&elem.cell.borrow().colspan_expr, component);
842 result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
843 }
844 }
845 result
846}
847
848fn row_runtime_child_count(
852 child_items: &[i_slint_compiler::layout::RowChildTemplate],
853 sub_instance_ref: InstanceRef,
854) -> usize {
855 let mut count = 0;
856 for child in child_items {
857 if let Some(repeated_element) = child.repeated_element() {
858 count += repeater_instances(sub_instance_ref, repeated_element).len();
859 } else {
860 count += 1;
861 }
862 }
863 count
864}
865
866fn grid_repeater_indices(
867 grid_layout: &i_slint_compiler::layout::GridLayout,
868 ctx: &mut EvalLocalContext,
869 repeater_steps: &[u32],
870) -> Vec<u32> {
871 let component = ctx.component_instance;
872 let mut repeater_indices = Vec::new();
873 let mut num_cells = 0;
874 let mut step_idx = 0;
875 for elem in grid_layout.elems.iter() {
876 if elem.item.element.borrow().repeated.is_some() {
877 let component_vec = repeater_instances(component, &elem.item.element);
878 repeater_indices.push(num_cells as _);
879 repeater_indices.push(component_vec.len() as _);
880 let item_count = repeater_steps[step_idx] as usize;
881 num_cells += component_vec.len() * item_count;
882 step_idx += 1;
883 } else {
884 num_cells += 1;
885 }
886 }
887 repeater_indices
888}
889
890fn grid_repeater_steps(
891 grid_layout: &i_slint_compiler::layout::GridLayout,
892 ctx: &mut EvalLocalContext,
893) -> Vec<u32> {
894 let component = ctx.component_instance;
895 let mut repeater_steps = Vec::new();
896 for elem in grid_layout.elems.iter() {
897 if elem.item.element.borrow().repeated.is_some() {
898 let item_count = match &elem.cell.borrow().child_items {
899 Some(ci)
900 if ci.iter().any(i_slint_compiler::layout::RowChildTemplate::is_repeated) =>
901 {
902 let component_vec = repeater_instances(component, &elem.item.element);
904 component_vec
905 .iter()
906 .map(|sub| {
907 generativity::make_guard!(guard);
908 let sub_pin = sub.as_pin_ref();
909 let sub_ref =
910 unsafe { InstanceRef::from_pin_ref(sub_pin.borrow(), guard) };
911 row_runtime_child_count(ci, sub_ref)
912 })
913 .max()
914 .unwrap_or(0)
915 }
916 Some(ci) => ci.len(),
917 None => 1,
918 };
919 repeater_steps.push(item_count as u32);
920 }
921 }
922 repeater_steps
923}
924
925fn grid_layout_constraints(
926 grid_layout: &i_slint_compiler::layout::GridLayout,
927 orientation: Orientation,
928 ctx: &mut EvalLocalContext,
929 repeater_steps: &[u32],
930 cross_axis_size: Option<f32>,
931) -> Vec<core_layout::LayoutItemInfo> {
932 let component = ctx.component_instance;
933 let expr_eval = |nr: &NamedReference| -> f32 {
934 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
935 };
936 let mut constraints = Vec::with_capacity(grid_layout.elems.len());
937
938 let mut repeater_idx = 0usize;
939 for layout_elem in grid_layout.elems.iter() {
940 if layout_elem.item.element.borrow().repeated.is_some() {
941 let component_vec = repeater_instances(component, &layout_elem.item.element);
942 let child_items = layout_elem.cell.borrow().child_items.clone();
943 let has_children = child_items.is_some();
944 if has_children {
945 let ci = child_items.as_ref().unwrap();
947 let step = repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
948 for sub_comp in &component_vec {
949 let per_instance_start = constraints.len();
950 generativity::make_guard!(guard);
952 let sub_pin = sub_comp.as_pin_ref();
953 let sub_borrow = sub_pin.borrow();
954 let sub_instance_ref = unsafe { InstanceRef::from_pin_ref(sub_borrow, guard) };
955 let expr_eval = |nr: &NamedReference| -> f32 {
956 eval::load_property(sub_instance_ref, &nr.element(), nr.name())
957 .unwrap()
958 .try_into()
959 .unwrap()
960 };
961
962 for child_template in ci.iter() {
966 match child_template {
967 i_slint_compiler::layout::RowChildTemplate::Static(child_item) => {
968 let mut layout_info = crate::eval_layout::get_layout_info(
969 &child_item.element,
970 sub_instance_ref,
971 &sub_instance_ref.window_adapter(),
972 orientation,
973 );
974 fill_layout_info_constraints(
975 &mut layout_info,
976 &child_item.constraints,
977 orientation,
978 &expr_eval,
979 );
980 constraints
981 .push(core_layout::LayoutItemInfo { constraint: layout_info });
982 }
983 i_slint_compiler::layout::RowChildTemplate::Repeated {
984 item: child_item,
985 repeated_element,
986 } => {
987 let inner_instances =
989 repeater_instances(sub_instance_ref, repeated_element);
990 for inner_comp in &inner_instances {
991 let inner_pin = inner_comp.as_pin_ref();
992 let mut layout_info =
993 inner_pin.layout_item_info(to_runtime(orientation), None);
994 generativity::make_guard!(inner_guard);
997 let inner_borrow = inner_pin.borrow();
998 let inner_instance_ref = unsafe {
999 InstanceRef::from_pin_ref(inner_borrow, inner_guard)
1000 };
1001 let inner_expr_eval = |nr: &NamedReference| -> f32 {
1002 eval::load_property(
1003 inner_instance_ref,
1004 &nr.element(),
1005 nr.name(),
1006 )
1007 .unwrap()
1008 .try_into()
1009 .unwrap()
1010 };
1011 fill_layout_info_constraints(
1012 &mut layout_info.constraint,
1013 &child_item.constraints,
1014 orientation,
1015 &inner_expr_eval,
1016 );
1017 constraints.push(layout_info);
1018 }
1019 }
1020 }
1021 }
1022 let pushed = constraints.len() - per_instance_start;
1025 for _ in pushed..step {
1026 constraints.push(core_layout::LayoutItemInfo::default());
1027 }
1028 }
1029 } else {
1030 constraints.extend(
1032 component_vec
1033 .iter()
1034 .map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
1035 );
1036 }
1037 repeater_idx += 1;
1038 } else {
1039 let cross_axis =
1040 cross_axis_size_for_cell(&layout_elem.item.element, orientation, cross_axis_size);
1041 let mut layout_info = get_layout_info_with_constraint(
1042 &layout_elem.item.element,
1043 component,
1044 &component.window_adapter(),
1045 orientation,
1046 cross_axis,
1047 );
1048 fill_layout_info_constraints(
1049 &mut layout_info,
1050 &layout_elem.item.constraints,
1051 orientation,
1052 &expr_eval,
1053 );
1054 constraints.push(core_layout::LayoutItemInfo { constraint: layout_info });
1055 }
1056 }
1057 constraints
1058}
1059
1060fn box_layout_data(
1062 box_layout: &i_slint_compiler::layout::BoxLayout,
1063 orientation: Orientation,
1064 component: InstanceRef,
1065 expr_eval: &impl Fn(&NamedReference) -> f32,
1066 mut repeater_indices: Option<&mut Vec<u32>>,
1067 cross_axis_size: Option<f32>,
1068) -> (Vec<core_layout::LayoutItemInfo>, i_slint_core::items::LayoutAlignment) {
1069 let window_adapter = component.window_adapter();
1070 let mut cells = Vec::with_capacity(box_layout.elems.len());
1071 for cell in &box_layout.elems {
1072 if cell.element.borrow().repeated.is_some() {
1073 let component_vec = repeater_instances(component, &cell.element);
1075 if let Some(ri) = repeater_indices.as_mut() {
1076 ri.push(cells.len() as _);
1077 ri.push(component_vec.len() as _);
1078 }
1079 cells.extend(
1080 component_vec
1081 .iter()
1082 .map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
1083 );
1084 } else {
1085 let cross_axis = cross_axis_size_for_cell(&cell.element, orientation, cross_axis_size);
1087 let mut layout_info = get_layout_info_with_constraint(
1088 &cell.element,
1089 component,
1090 &window_adapter,
1091 orientation,
1092 cross_axis,
1093 );
1094 fill_layout_info_constraints(
1095 &mut layout_info,
1096 &cell.constraints,
1097 orientation,
1098 &expr_eval,
1099 );
1100 cells.push(core_layout::LayoutItemInfo { constraint: layout_info });
1101 }
1102 }
1103 let alignment = box_layout
1104 .geometry
1105 .alignment
1106 .as_ref()
1107 .map(|nr| {
1108 eval::load_property(component, &nr.element(), nr.name())
1109 .unwrap()
1110 .try_into()
1111 .unwrap_or_default()
1112 })
1113 .unwrap_or_default();
1114 (cells, alignment)
1115}
1116
1117pub(crate) fn fill_layout_info_constraints(
1118 layout_info: &mut core_layout::LayoutInfo,
1119 constraints: &LayoutConstraints,
1120 orientation: Orientation,
1121 expr_eval: &impl Fn(&NamedReference) -> f32,
1122) {
1123 let is_percent =
1124 |nr: &NamedReference| Expression::PropertyReference(nr.clone()).ty() == Type::Percent;
1125
1126 match orientation {
1127 Orientation::Horizontal => {
1128 if let Some(e) = constraints.min_width.as_ref() {
1129 if !is_percent(e) {
1130 layout_info.min = expr_eval(e)
1131 } else {
1132 layout_info.min_percent = expr_eval(e)
1133 }
1134 }
1135 if let Some(e) = constraints.max_width.as_ref() {
1136 if !is_percent(e) {
1137 layout_info.max = expr_eval(e)
1138 } else {
1139 layout_info.max_percent = expr_eval(e)
1140 }
1141 }
1142 if let Some(e) = constraints.preferred_width.as_ref() {
1143 layout_info.preferred = expr_eval(e);
1144 }
1145 if let Some(e) = constraints.horizontal_stretch.as_ref() {
1146 layout_info.stretch = expr_eval(e);
1147 }
1148 }
1149 Orientation::Vertical => {
1150 if let Some(e) = constraints.min_height.as_ref() {
1151 if !is_percent(e) {
1152 layout_info.min = expr_eval(e)
1153 } else {
1154 layout_info.min_percent = expr_eval(e)
1155 }
1156 }
1157 if let Some(e) = constraints.max_height.as_ref() {
1158 if !is_percent(e) {
1159 layout_info.max = expr_eval(e)
1160 } else {
1161 layout_info.max_percent = expr_eval(e)
1162 }
1163 }
1164 if let Some(e) = constraints.preferred_height.as_ref() {
1165 layout_info.preferred = expr_eval(e);
1166 }
1167 if let Some(e) = constraints.vertical_stretch.as_ref() {
1168 layout_info.stretch = expr_eval(e);
1169 }
1170 }
1171 }
1172}
1173
1174pub(crate) fn get_layout_info(
1176 elem: &ElementRc,
1177 component: InstanceRef,
1178 window_adapter: &Rc<dyn WindowAdapter>,
1179 orientation: Orientation,
1180) -> core_layout::LayoutInfo {
1181 get_layout_info_with_constraint(elem, component, window_adapter, orientation, None)
1182}
1183
1184pub(crate) fn get_layout_info_with_constraint(
1185 elem: &ElementRc,
1186 component: InstanceRef,
1187 window_adapter: &Rc<dyn WindowAdapter>,
1188 orientation: Orientation,
1189 cross_axis_constraint: Option<f32>,
1190) -> core_layout::LayoutInfo {
1191 let parameterized_nr = if cross_axis_constraint.is_some() {
1197 match orientation {
1198 Orientation::Vertical => elem.borrow().inherited_layout_info_v_with_constraint(),
1199 Orientation::Horizontal => elem.borrow().inherited_layout_info_h_with_constraint(),
1200 }
1201 } else {
1202 None
1203 };
1204 if let Some(nr) = parameterized_nr {
1205 let arg = cross_axis_constraint.unwrap();
1206 let v = eval::call_function(
1207 &eval::ComponentInstance::InstanceRef(component),
1208 &nr.element(),
1209 nr.name(),
1210 vec![Value::Number(arg as f64)],
1211 )
1212 .expect("layoutinfo-{h,v}-with-constraint is a synthesized pure function");
1213 return v.try_into().unwrap();
1214 }
1215
1216 let elem = elem.borrow();
1217 if let Some(nr) = elem.layout_info_prop(orientation) {
1218 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
1219 } else {
1220 let item = &component
1221 .description
1222 .items
1223 .get(elem.id.as_str())
1224 .unwrap_or_else(|| panic!("Internal error: Item {} not found", elem.id));
1225 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
1226
1227 unsafe {
1228 item.item_from_item_tree(component.as_ptr()).as_ref().layout_info(
1229 to_runtime(orientation),
1230 cross_axis_constraint.unwrap_or(-1.),
1231 window_adapter,
1232 &ItemRc::new(vtable::VRc::into_dyn(item_comp), item.item_index()),
1233 )
1234 }
1235 }
1236}
1237
1238fn cross_axis_size_for_cell(
1245 elem: &ElementRc,
1246 orientation: Orientation,
1247 parent_cross_axis_size: Option<f32>,
1248) -> Option<f32> {
1249 let cross = parent_cross_axis_size?;
1250 let elem_b = elem.borrow();
1251 if orientation == Orientation::Horizontal {
1252 return elem_b.inherited_layout_info_h_with_constraint().is_some().then_some(cross);
1257 }
1258 if elem_b.layout_info_v_with_constraint.is_some() {
1259 return Some(cross);
1260 }
1261 if elem_b.layout_info_prop(Orientation::Vertical).is_none() {
1266 return Some(cross);
1267 }
1268 None
1269}