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 );
240
241 let alignment = flexbox_layout
242 .geometry
243 .alignment
244 .as_ref()
245 .map_or(i_slint_core::items::LayoutAlignment::default(), |nr| {
246 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
247 });
248 let align_content = flexbox_layout
249 .align_content
250 .as_ref()
251 .map_or(i_slint_core::items::FlexboxLayoutAlignContent::default(), |nr| {
252 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
253 });
254 let align_items = flexbox_layout
255 .align_items
256 .as_ref()
257 .map_or(i_slint_core::items::LayoutAlignItems::default(), |nr| {
258 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
259 });
260 let flex_wrap = flexbox_layout
261 .flex_wrap
262 .as_ref()
263 .map_or(i_slint_core::items::FlexboxLayoutWrap::default(), |nr| {
264 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
265 });
266
267 let (padding_h, spacing_h) =
268 padding_and_spacing(&flexbox_layout.geometry, Orientation::Horizontal, &expr_eval);
269 let (padding_v, spacing_v) =
270 padding_and_spacing(&flexbox_layout.geometry, Orientation::Vertical, &expr_eval);
271
272 let data = core_layout::FlexboxLayoutData {
273 width: width_ref.as_ref().map(&expr_eval).unwrap_or(0.),
274 height: height_ref.as_ref().map(&expr_eval).unwrap_or(0.),
275 spacing_h,
276 spacing_v,
277 padding_h,
278 padding_v,
279 alignment,
280 direction,
281 align_content,
282 align_items,
283 flex_wrap,
284 cells_h: Slice::from(cells_h.as_slice()),
285 cells_v: Slice::from(cells_v.as_slice()),
286 };
287 let ri = Slice::from(repeated_indices.as_slice());
288
289 let window_adapter = component.window_adapter();
291 let mut child_elem_ids: Vec<Option<smol_str::SmolStr>> = Vec::new();
292 for layout_elem in &flexbox_layout.elems {
293 if layout_elem.item.element.borrow().repeated.is_some() {
294 let component_vec = repeater_instances(component, &layout_elem.item.element);
295 for _ in 0..component_vec.len() {
296 child_elem_ids.push(None);
297 }
298 } else {
299 child_elem_ids.push(Some(layout_elem.item.element.borrow().id.clone()));
300 }
301 }
302
303 let mut measure = |child_index: usize,
308 known_w: Option<f32>,
309 known_h: Option<f32>|
310 -> (f32, f32) {
311 let default_w = cells_h.get(child_index).map_or(0., |c| c.constraint.preferred_bounded());
312 let default_h = cells_v.get(child_index).map_or(0., |c| c.constraint.preferred_bounded());
313 let w = known_w.unwrap_or(default_w);
314 let h = known_h.unwrap_or(default_h);
315
316 let elem_id = match child_elem_ids.get(child_index) {
317 Some(Some(id)) => id,
318 _ => return (w, h),
319 };
320 let item_within = match component.description.items.get(elem_id.as_str()) {
321 Some(i) => i,
322 None => return (w, h),
323 };
324
325 if known_w.is_some() && known_h.is_none() {
327 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
328 let item_rc = ItemRc::new(vtable::VRc::into_dyn(item_comp), item_within.item_index());
329 let item = unsafe { item_within.item_from_item_tree(component.as_ptr()) };
330 let v_info = item.as_ref().layout_info(
331 to_runtime(Orientation::Vertical),
332 w,
333 &window_adapter,
334 &item_rc,
335 );
336 return (w, v_info.preferred_bounded());
337 }
338 if known_h.is_some() && known_w.is_none() {
339 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
340 let item_rc = ItemRc::new(vtable::VRc::into_dyn(item_comp), item_within.item_index());
341 let item = unsafe { item_within.item_from_item_tree(component.as_ptr()) };
342 let h_info = item.as_ref().layout_info(
343 to_runtime(Orientation::Horizontal),
344 h,
345 &window_adapter,
346 &item_rc,
347 );
348 return (h_info.preferred_bounded(), h);
349 }
350 (w, h)
351 };
352
353 core_layout::solve_flexbox_layout_with_measure(&data, ri, Some(&mut measure)).into()
354}
355
356fn flexbox_layout_direction(
357 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
358 local_context: &EvalLocalContext,
359) -> FlexboxLayoutDirection {
360 flexbox_layout
361 .direction
362 .as_ref()
363 .and_then(|nr| {
364 let value =
365 eval::load_property(local_context.component_instance, &nr.element(), nr.name())
366 .ok()?;
367 if let Value::EnumerationValue(_, variant) = &value {
368 match variant.as_str() {
369 "row" => Some(FlexboxLayoutDirection::Row),
370 "row-reverse" => Some(FlexboxLayoutDirection::RowReverse),
371 "column" => Some(FlexboxLayoutDirection::Column),
372 "column-reverse" => Some(FlexboxLayoutDirection::ColumnReverse),
373 _ => None,
374 }
375 } else {
376 None
377 }
378 })
379 .unwrap_or(FlexboxLayoutDirection::Row)
380}
381
382pub(crate) fn compute_flexbox_layout_info(
383 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
384 orientation: Orientation,
385 local_context: &mut EvalLocalContext,
386 cross_axis_size: Option<f32>,
387) -> Value {
388 let component = local_context.component_instance;
389 let expr_eval = |nr: &NamedReference| -> f32 {
390 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
391 };
392
393 let (cells_h, cells_v, _repeated_indices) =
394 flexbox_layout_data(flexbox_layout, component, &expr_eval, local_context, cross_axis_size);
395
396 let direction = flexbox_layout_direction(flexbox_layout, local_context);
398
399 let is_main_axis = matches!(
401 (direction, orientation),
402 (FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse, Orientation::Horizontal)
403 | (
404 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse,
405 Orientation::Vertical
406 )
407 );
408
409 let (padding_h, spacing_h) =
410 padding_and_spacing(&flexbox_layout.geometry, Orientation::Horizontal, &expr_eval);
411 let (padding_v, spacing_v) =
412 padding_and_spacing(&flexbox_layout.geometry, Orientation::Vertical, &expr_eval);
413
414 let flex_wrap = flexbox_layout
415 .flex_wrap
416 .as_ref()
417 .map_or(i_slint_core::items::FlexboxLayoutWrap::default(), |nr| {
418 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
419 });
420
421 if is_main_axis {
422 let (cells, spacing, padding) = match orientation {
423 Orientation::Horizontal => (&cells_h, spacing_h, &padding_h),
424 Orientation::Vertical => (&cells_v, spacing_v, &padding_v),
425 };
426 core_layout::flexbox_layout_info_main_axis(
427 Slice::from(cells.as_slice()),
428 spacing,
429 padding,
430 flex_wrap,
431 )
432 .into()
433 } else {
434 let constraint_size = match orientation {
438 Orientation::Horizontal => {
439 let height_ref = &flexbox_layout.geometry.rect.height_reference;
440 height_ref.as_ref().map(&expr_eval).unwrap_or(0.)
441 }
442 Orientation::Vertical => {
443 let width_ref = &flexbox_layout.geometry.rect.width_reference;
444 width_ref.as_ref().map(&expr_eval).unwrap_or(0.)
445 }
446 };
447 core_layout::flexbox_layout_info_cross_axis(
448 Slice::from(cells_h.as_slice()),
449 Slice::from(cells_v.as_slice()),
450 spacing_h,
451 spacing_v,
452 &padding_h,
453 &padding_v,
454 direction,
455 flex_wrap,
456 constraint_size,
457 )
458 .into()
459 }
460}
461
462fn flexbox_layout_data(
463 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
464 component: InstanceRef,
465 expr_eval: &impl Fn(&NamedReference) -> f32,
466 _local_context: &mut EvalLocalContext,
467 container_width: Option<f32>,
468) -> (Vec<core_layout::FlexboxLayoutItemInfo>, Vec<core_layout::FlexboxLayoutItemInfo>, Vec<u32>) {
469 let window_adapter = component.window_adapter();
470 let mut cells_h = Vec::with_capacity(flexbox_layout.elems.len());
471 let mut cells_v = Vec::with_capacity(flexbox_layout.elems.len());
472 let mut repeated_indices = Vec::new();
473
474 struct ChildInfo {
477 flex_grow: f32,
478 flex_shrink: f32,
479 flex_basis: f32,
480 flex_align_self: i_slint_core::items::FlexboxLayoutAlignSelf,
481 flex_order: i32,
482 }
483 let mut static_children: Vec<Option<ChildInfo>> = Vec::new(); for layout_elem in &flexbox_layout.elems {
486 if layout_elem.item.element.borrow().repeated.is_some() {
487 let component_vec = repeater_instances(component, &layout_elem.item.element);
488 repeated_indices.push(cells_h.len() as u32);
489 repeated_indices.push(component_vec.len() as u32);
490 cells_h.extend(component_vec.iter().map(|x| {
491 x.as_pin_ref().flexbox_layout_item_info(to_runtime(Orientation::Horizontal), None)
492 }));
493 cells_v.extend(component_vec.iter().map(|x| {
494 x.as_pin_ref().flexbox_layout_item_info(to_runtime(Orientation::Vertical), None)
495 }));
496 for _ in 0..component_vec.len() {
497 static_children.push(None);
498 }
499 } else {
500 let mut layout_info_h = get_layout_info(
501 &layout_elem.item.element,
502 component,
503 &window_adapter,
504 Orientation::Horizontal,
505 );
506 fill_layout_info_constraints(
507 &mut layout_info_h,
508 &layout_elem.item.constraints,
509 Orientation::Horizontal,
510 expr_eval,
511 );
512 let flex_grow = layout_elem.flex_grow.as_ref().map(&expr_eval).unwrap_or(0.0);
516 let flex_shrink = layout_elem.flex_shrink.as_ref().map(&expr_eval).unwrap_or(1.0);
517 let flex_basis = layout_elem.flex_basis.as_ref().map(&expr_eval).unwrap_or(-1.0);
518 let align_self = layout_elem
519 .align_self
520 .as_ref()
521 .map(|nr| {
522 eval::load_property(component, &nr.element(), nr.name())
523 .unwrap()
524 .try_into()
525 .unwrap()
526 })
527 .unwrap_or(i_slint_core::items::FlexboxLayoutAlignSelf::default());
528 let order = layout_elem.order.as_ref().map(expr_eval).unwrap_or(0.0) as i32;
529 cells_h.push(core_layout::FlexboxLayoutItemInfo {
530 constraint: layout_info_h,
531 flex_grow,
532 flex_shrink,
533 flex_basis,
534 flex_align_self: align_self,
535 flex_order: order,
536 });
537 cells_v.push(core_layout::FlexboxLayoutItemInfo::default());
539 static_children.push(Some(ChildInfo {
540 flex_grow,
541 flex_shrink,
542 flex_basis,
543 flex_align_self: align_self,
544 flex_order: order,
545 }));
546 }
547 }
548
549 let mut cell_idx = 0usize;
553 for layout_elem in &flexbox_layout.elems {
554 if layout_elem.item.element.borrow().repeated.is_some() {
555 let component_vec = repeater_instances(component, &layout_elem.item.element);
556 cell_idx += component_vec.len();
557 } else {
559 let width_constraint =
560 container_width.unwrap_or_else(|| cells_h[cell_idx].constraint.preferred_bounded());
561 let mut layout_info_v = get_layout_info_with_constraint(
562 &layout_elem.item.element,
563 component,
564 &window_adapter,
565 Orientation::Vertical,
566 Some(width_constraint),
567 );
568 fill_layout_info_constraints(
569 &mut layout_info_v,
570 &layout_elem.item.constraints,
571 Orientation::Vertical,
572 expr_eval,
573 );
574 if let Some(info) = &static_children[cell_idx] {
575 cells_v[cell_idx] = core_layout::FlexboxLayoutItemInfo {
576 constraint: layout_info_v,
577 flex_grow: info.flex_grow,
578 flex_shrink: info.flex_shrink,
579 flex_basis: info.flex_basis,
580 flex_align_self: info.flex_align_self,
581 flex_order: info.flex_order,
582 };
583 }
584 cell_idx += 1;
585 }
586 }
587
588 (cells_h, cells_v, repeated_indices)
589}
590
591fn padding_and_spacing(
593 layout_geometry: &LayoutGeometry,
594 orientation: Orientation,
595 expr_eval: &impl Fn(&NamedReference) -> f32,
596) -> (core_layout::Padding, f32) {
597 let spacing = layout_geometry.spacing.orientation(orientation).map_or(0., expr_eval);
598 let (begin, end) = layout_geometry.padding.begin_end(orientation);
599 let padding =
600 core_layout::Padding { begin: begin.map_or(0., expr_eval), end: end.map_or(0., expr_eval) };
601 (padding, spacing)
602}
603
604fn repeater_instances(
605 component: InstanceRef,
606 elem: &ElementRc,
607) -> Vec<crate::dynamic_item_tree::DynamicComponentVRc> {
608 generativity::make_guard!(guard);
609 let rep =
610 crate::dynamic_item_tree::get_repeater_by_name(component, elem.borrow().id.as_str(), guard);
611 rep.0.as_ref().track_instance_changes();
612 rep.0.as_ref().instances_vec()
613}
614
615fn grid_layout_input_data(
616 grid_layout: &i_slint_compiler::layout::GridLayout,
617 ctx: &EvalLocalContext,
618 repeater_steps: &[u32],
619) -> Vec<GridLayoutInputData> {
620 let component = ctx.component_instance;
621 let mut result = Vec::with_capacity(grid_layout.elems.len());
622 let mut after_repeater_in_same_row = false;
623 let mut new_row = true;
624 let mut repeater_idx = 0usize;
625 for elem in grid_layout.elems.iter() {
626 let eval_or_default = |expr: &RowColExpr, component: InstanceRef| match expr {
627 RowColExpr::Literal(value) => *value as f32,
628 RowColExpr::Auto => i_slint_common::ROW_COL_AUTO,
629 RowColExpr::Named(nr) => {
630 eval::load_property(component, &nr.element(), nr.name())
632 .unwrap()
633 .try_into()
634 .unwrap()
635 }
636 };
637
638 let cell_new_row = elem.cell.borrow().new_row;
639 if cell_new_row {
640 after_repeater_in_same_row = false;
641 }
642 if elem.item.element.borrow().repeated.is_some() {
643 let component_vec = repeater_instances(component, &elem.item.element);
644 new_row = cell_new_row;
645 for erased_sub_comp in &component_vec {
646 generativity::make_guard!(guard);
648 let sub_comp = erased_sub_comp.as_pin_ref();
649 let sub_instance_ref =
650 unsafe { InstanceRef::from_pin_ref(sub_comp.borrow(), guard) };
651
652 if let Some(children) = elem.cell.borrow().child_items.as_ref() {
653 new_row = true;
655 let start_count = result.len();
656
657 for child_template in children {
663 match child_template {
664 i_slint_compiler::layout::RowChildTemplate::Static(child_item) => {
665 let (row_val, col_val, rowspan_val, colspan_val) = {
666 let element_ref = child_item.element.borrow();
667 let child_cell =
668 element_ref.grid_layout_cell.as_ref().unwrap().borrow();
669 (
670 eval_or_default(&child_cell.row_expr, sub_instance_ref),
671 eval_or_default(&child_cell.col_expr, sub_instance_ref),
672 eval_or_default(&child_cell.rowspan_expr, sub_instance_ref),
673 eval_or_default(&child_cell.colspan_expr, sub_instance_ref),
674 )
675 };
676 result.push(GridLayoutInputData {
677 new_row,
678 col: col_val,
679 row: row_val,
680 colspan: colspan_val,
681 rowspan: rowspan_val,
682 });
683 new_row = false;
684 }
685 i_slint_compiler::layout::RowChildTemplate::Repeated {
686 repeated_element,
687 ..
688 } => {
689 let inner_instances =
690 repeater_instances(sub_instance_ref, repeated_element);
691 for i in 0..inner_instances.len() {
692 result.push(GridLayoutInputData {
693 new_row: i == 0 && new_row,
694 ..Default::default()
695 });
696 }
697 if !inner_instances.is_empty() {
698 new_row = false;
699 }
700 }
701 }
702 }
703 let cells_pushed = result.len() - start_count;
705 let expected_step =
706 repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
707 for _ in cells_pushed..expected_step {
708 result.push(GridLayoutInputData::default());
709 }
710 } else {
711 let cell = elem.cell.borrow();
713 let row = eval_or_default(&cell.row_expr, sub_instance_ref);
714 let col = eval_or_default(&cell.col_expr, sub_instance_ref);
715 let rowspan = eval_or_default(&cell.rowspan_expr, sub_instance_ref);
716 let colspan = eval_or_default(&cell.colspan_expr, sub_instance_ref);
717 result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
718 new_row = false;
719 }
720 }
721 repeater_idx += 1;
722 after_repeater_in_same_row = true;
723 } else {
724 let new_row =
725 if cell_new_row || !after_repeater_in_same_row { cell_new_row } else { new_row };
726 let row = eval_or_default(&elem.cell.borrow().row_expr, component);
727 let col = eval_or_default(&elem.cell.borrow().col_expr, component);
728 let rowspan = eval_or_default(&elem.cell.borrow().rowspan_expr, component);
729 let colspan = eval_or_default(&elem.cell.borrow().colspan_expr, component);
730 result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
731 }
732 }
733 result
734}
735
736fn row_runtime_child_count(
740 child_items: &[i_slint_compiler::layout::RowChildTemplate],
741 sub_instance_ref: InstanceRef,
742) -> usize {
743 let mut count = 0;
744 for child in child_items {
745 if let Some(repeated_element) = child.repeated_element() {
746 count += repeater_instances(sub_instance_ref, repeated_element).len();
747 } else {
748 count += 1;
749 }
750 }
751 count
752}
753
754fn grid_repeater_indices(
755 grid_layout: &i_slint_compiler::layout::GridLayout,
756 ctx: &mut EvalLocalContext,
757 repeater_steps: &[u32],
758) -> Vec<u32> {
759 let component = ctx.component_instance;
760 let mut repeater_indices = Vec::new();
761 let mut num_cells = 0;
762 let mut step_idx = 0;
763 for elem in grid_layout.elems.iter() {
764 if elem.item.element.borrow().repeated.is_some() {
765 let component_vec = repeater_instances(component, &elem.item.element);
766 repeater_indices.push(num_cells as _);
767 repeater_indices.push(component_vec.len() as _);
768 let item_count = repeater_steps[step_idx] as usize;
769 num_cells += component_vec.len() * item_count;
770 step_idx += 1;
771 } else {
772 num_cells += 1;
773 }
774 }
775 repeater_indices
776}
777
778fn grid_repeater_steps(
779 grid_layout: &i_slint_compiler::layout::GridLayout,
780 ctx: &mut EvalLocalContext,
781) -> Vec<u32> {
782 let component = ctx.component_instance;
783 let mut repeater_steps = Vec::new();
784 for elem in grid_layout.elems.iter() {
785 if elem.item.element.borrow().repeated.is_some() {
786 let item_count = match &elem.cell.borrow().child_items {
787 Some(ci)
788 if ci.iter().any(i_slint_compiler::layout::RowChildTemplate::is_repeated) =>
789 {
790 let component_vec = repeater_instances(component, &elem.item.element);
792 component_vec
793 .iter()
794 .map(|sub| {
795 generativity::make_guard!(guard);
796 let sub_pin = sub.as_pin_ref();
797 let sub_ref =
798 unsafe { InstanceRef::from_pin_ref(sub_pin.borrow(), guard) };
799 row_runtime_child_count(ci, sub_ref)
800 })
801 .max()
802 .unwrap_or(0)
803 }
804 Some(ci) => ci.len(),
805 None => 1,
806 };
807 repeater_steps.push(item_count as u32);
808 }
809 }
810 repeater_steps
811}
812
813fn grid_layout_constraints(
814 grid_layout: &i_slint_compiler::layout::GridLayout,
815 orientation: Orientation,
816 ctx: &mut EvalLocalContext,
817 repeater_steps: &[u32],
818 cross_axis_size: Option<f32>,
819) -> Vec<core_layout::LayoutItemInfo> {
820 let component = ctx.component_instance;
821 let expr_eval = |nr: &NamedReference| -> f32 {
822 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
823 };
824 let mut constraints = Vec::with_capacity(grid_layout.elems.len());
825
826 let mut repeater_idx = 0usize;
827 for layout_elem in grid_layout.elems.iter() {
828 if layout_elem.item.element.borrow().repeated.is_some() {
829 let component_vec = repeater_instances(component, &layout_elem.item.element);
830 let child_items = layout_elem.cell.borrow().child_items.clone();
831 let has_children = child_items.is_some();
832 if has_children {
833 let ci = child_items.as_ref().unwrap();
835 let step = repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
836 for sub_comp in &component_vec {
837 let per_instance_start = constraints.len();
838 generativity::make_guard!(guard);
840 let sub_pin = sub_comp.as_pin_ref();
841 let sub_borrow = sub_pin.borrow();
842 let sub_instance_ref = unsafe { InstanceRef::from_pin_ref(sub_borrow, guard) };
843 let expr_eval = |nr: &NamedReference| -> f32 {
844 eval::load_property(sub_instance_ref, &nr.element(), nr.name())
845 .unwrap()
846 .try_into()
847 .unwrap()
848 };
849
850 for child_template in ci.iter() {
854 match child_template {
855 i_slint_compiler::layout::RowChildTemplate::Static(child_item) => {
856 let mut layout_info = crate::eval_layout::get_layout_info(
857 &child_item.element,
858 sub_instance_ref,
859 &sub_instance_ref.window_adapter(),
860 orientation,
861 );
862 fill_layout_info_constraints(
863 &mut layout_info,
864 &child_item.constraints,
865 orientation,
866 &expr_eval,
867 );
868 constraints
869 .push(core_layout::LayoutItemInfo { constraint: layout_info });
870 }
871 i_slint_compiler::layout::RowChildTemplate::Repeated {
872 item: child_item,
873 repeated_element,
874 } => {
875 let inner_instances =
877 repeater_instances(sub_instance_ref, repeated_element);
878 for inner_comp in &inner_instances {
879 let inner_pin = inner_comp.as_pin_ref();
880 let mut layout_info =
881 inner_pin.layout_item_info(to_runtime(orientation), None);
882 generativity::make_guard!(inner_guard);
885 let inner_borrow = inner_pin.borrow();
886 let inner_instance_ref = unsafe {
887 InstanceRef::from_pin_ref(inner_borrow, inner_guard)
888 };
889 let inner_expr_eval = |nr: &NamedReference| -> f32 {
890 eval::load_property(
891 inner_instance_ref,
892 &nr.element(),
893 nr.name(),
894 )
895 .unwrap()
896 .try_into()
897 .unwrap()
898 };
899 fill_layout_info_constraints(
900 &mut layout_info.constraint,
901 &child_item.constraints,
902 orientation,
903 &inner_expr_eval,
904 );
905 constraints.push(layout_info);
906 }
907 }
908 }
909 }
910 let pushed = constraints.len() - per_instance_start;
913 for _ in pushed..step {
914 constraints.push(core_layout::LayoutItemInfo::default());
915 }
916 }
917 } else {
918 constraints.extend(
920 component_vec
921 .iter()
922 .map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
923 );
924 }
925 repeater_idx += 1;
926 } else {
927 let cross_axis =
928 cross_axis_size_for_cell(&layout_elem.item.element, orientation, cross_axis_size);
929 let mut layout_info = get_layout_info_with_constraint(
930 &layout_elem.item.element,
931 component,
932 &component.window_adapter(),
933 orientation,
934 cross_axis,
935 );
936 fill_layout_info_constraints(
937 &mut layout_info,
938 &layout_elem.item.constraints,
939 orientation,
940 &expr_eval,
941 );
942 constraints.push(core_layout::LayoutItemInfo { constraint: layout_info });
943 }
944 }
945 constraints
946}
947
948fn box_layout_data(
950 box_layout: &i_slint_compiler::layout::BoxLayout,
951 orientation: Orientation,
952 component: InstanceRef,
953 expr_eval: &impl Fn(&NamedReference) -> f32,
954 mut repeater_indices: Option<&mut Vec<u32>>,
955 cross_axis_size: Option<f32>,
956) -> (Vec<core_layout::LayoutItemInfo>, i_slint_core::items::LayoutAlignment) {
957 let window_adapter = component.window_adapter();
958 let mut cells = Vec::with_capacity(box_layout.elems.len());
959 for cell in &box_layout.elems {
960 if cell.element.borrow().repeated.is_some() {
961 let component_vec = repeater_instances(component, &cell.element);
963 if let Some(ri) = repeater_indices.as_mut() {
964 ri.push(cells.len() as _);
965 ri.push(component_vec.len() as _);
966 }
967 cells.extend(
968 component_vec
969 .iter()
970 .map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
971 );
972 } else {
973 let cross_axis = cross_axis_size_for_cell(&cell.element, orientation, cross_axis_size);
975 let mut layout_info = get_layout_info_with_constraint(
976 &cell.element,
977 component,
978 &window_adapter,
979 orientation,
980 cross_axis,
981 );
982 fill_layout_info_constraints(
983 &mut layout_info,
984 &cell.constraints,
985 orientation,
986 &expr_eval,
987 );
988 cells.push(core_layout::LayoutItemInfo { constraint: layout_info });
989 }
990 }
991 let alignment = box_layout
992 .geometry
993 .alignment
994 .as_ref()
995 .map(|nr| {
996 eval::load_property(component, &nr.element(), nr.name())
997 .unwrap()
998 .try_into()
999 .unwrap_or_default()
1000 })
1001 .unwrap_or_default();
1002 (cells, alignment)
1003}
1004
1005pub(crate) fn fill_layout_info_constraints(
1006 layout_info: &mut core_layout::LayoutInfo,
1007 constraints: &LayoutConstraints,
1008 orientation: Orientation,
1009 expr_eval: &impl Fn(&NamedReference) -> f32,
1010) {
1011 let is_percent =
1012 |nr: &NamedReference| Expression::PropertyReference(nr.clone()).ty() == Type::Percent;
1013
1014 match orientation {
1015 Orientation::Horizontal => {
1016 if let Some(e) = constraints.min_width.as_ref() {
1017 if !is_percent(e) {
1018 layout_info.min = expr_eval(e)
1019 } else {
1020 layout_info.min_percent = expr_eval(e)
1021 }
1022 }
1023 if let Some(e) = constraints.max_width.as_ref() {
1024 if !is_percent(e) {
1025 layout_info.max = expr_eval(e)
1026 } else {
1027 layout_info.max_percent = expr_eval(e)
1028 }
1029 }
1030 if let Some(e) = constraints.preferred_width.as_ref() {
1031 layout_info.preferred = expr_eval(e);
1032 }
1033 if let Some(e) = constraints.horizontal_stretch.as_ref() {
1034 layout_info.stretch = expr_eval(e);
1035 }
1036 }
1037 Orientation::Vertical => {
1038 if let Some(e) = constraints.min_height.as_ref() {
1039 if !is_percent(e) {
1040 layout_info.min = expr_eval(e)
1041 } else {
1042 layout_info.min_percent = expr_eval(e)
1043 }
1044 }
1045 if let Some(e) = constraints.max_height.as_ref() {
1046 if !is_percent(e) {
1047 layout_info.max = expr_eval(e)
1048 } else {
1049 layout_info.max_percent = expr_eval(e)
1050 }
1051 }
1052 if let Some(e) = constraints.preferred_height.as_ref() {
1053 layout_info.preferred = expr_eval(e);
1054 }
1055 if let Some(e) = constraints.vertical_stretch.as_ref() {
1056 layout_info.stretch = expr_eval(e);
1057 }
1058 }
1059 }
1060}
1061
1062pub(crate) fn get_layout_info(
1064 elem: &ElementRc,
1065 component: InstanceRef,
1066 window_adapter: &Rc<dyn WindowAdapter>,
1067 orientation: Orientation,
1068) -> core_layout::LayoutInfo {
1069 get_layout_info_with_constraint(elem, component, window_adapter, orientation, None)
1070}
1071
1072pub(crate) fn get_layout_info_with_constraint(
1073 elem: &ElementRc,
1074 component: InstanceRef,
1075 window_adapter: &Rc<dyn WindowAdapter>,
1076 orientation: Orientation,
1077 cross_axis_constraint: Option<f32>,
1078) -> core_layout::LayoutInfo {
1079 let constrained_nr = if orientation == Orientation::Vertical && cross_axis_constraint.is_some()
1084 {
1085 elem.borrow().layout_info_v_with_constraint.clone()
1086 } else {
1087 None
1088 };
1089 if let Some(constrained_nr) = constrained_nr {
1090 let width = cross_axis_constraint.unwrap();
1091 let v = eval::call_function(
1092 &eval::ComponentInstance::InstanceRef(component),
1093 &constrained_nr.element(),
1094 constrained_nr.name(),
1095 vec![Value::Number(width as f64)],
1096 )
1097 .expect("layoutinfo-v-with-constraint is a synthesized pure function");
1098 return v.try_into().unwrap();
1099 }
1100
1101 let elem = elem.borrow();
1102 if let Some(nr) = elem.layout_info_prop(orientation) {
1103 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
1104 } else {
1105 let item = &component
1106 .description
1107 .items
1108 .get(elem.id.as_str())
1109 .unwrap_or_else(|| panic!("Internal error: Item {} not found", elem.id));
1110 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
1111
1112 unsafe {
1113 item.item_from_item_tree(component.as_ptr()).as_ref().layout_info(
1114 to_runtime(orientation),
1115 cross_axis_constraint.unwrap_or(-1.),
1116 window_adapter,
1117 &ItemRc::new(vtable::VRc::into_dyn(item_comp), item.item_index()),
1118 )
1119 }
1120 }
1121}
1122
1123fn cross_axis_size_for_cell(
1129 elem: &ElementRc,
1130 orientation: Orientation,
1131 parent_cross_axis_size: Option<f32>,
1132) -> Option<f32> {
1133 if orientation != Orientation::Vertical {
1134 return None;
1135 }
1136 let width = parent_cross_axis_size?;
1137 let elem_b = elem.borrow();
1138 if elem_b.layout_info_v_with_constraint.is_some() {
1139 return Some(width);
1140 }
1141 if elem_b.layout_info_prop(Orientation::Vertical).is_none() {
1146 return Some(width);
1147 }
1148 None
1149}