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