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) -> Value {
42 let component = local_context.component_instance;
43 let expr_eval = |nr: &NamedReference| -> f32 {
44 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
45 };
46 let (padding, spacing) = padding_and_spacing(&grid_layout.geometry, orientation, &expr_eval);
47 let repeater_steps = grid_repeater_steps(grid_layout, local_context);
48 let repeater_indices = grid_repeater_indices(grid_layout, local_context, &repeater_steps);
49 let constraints =
50 grid_layout_constraints(grid_layout, orientation, local_context, &repeater_steps);
51 core_layout::grid_layout_info(
52 organized_data.clone(),
53 Slice::from_slice(constraints.as_slice()),
54 Slice::from_slice(repeater_indices.as_slice()),
55 Slice::from_slice(repeater_steps.as_slice()),
56 spacing,
57 &padding,
58 to_runtime(orientation),
59 )
60 .into()
61}
62
63pub(crate) fn compute_box_layout_info(
65 box_layout: &BoxLayout,
66 orientation: Orientation,
67 local_context: &mut EvalLocalContext,
68) -> Value {
69 let component = local_context.component_instance;
70 let expr_eval = |nr: &NamedReference| -> f32 {
71 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
72 };
73 let (cells, alignment) = box_layout_data(box_layout, orientation, component, &expr_eval, None);
74 let (padding, spacing) = padding_and_spacing(&box_layout.geometry, orientation, &expr_eval);
75 if orientation == box_layout.orientation {
76 core_layout::box_layout_info(Slice::from(cells.as_slice()), spacing, &padding, alignment)
77 } else {
78 core_layout::box_layout_info_ortho(Slice::from(cells.as_slice()), &padding)
79 }
80 .into()
81}
82
83pub(crate) fn organize_grid_layout(
84 layout: &GridLayout,
85 local_context: &mut EvalLocalContext,
86) -> Value {
87 let repeater_steps = grid_repeater_steps(layout, local_context);
88 let cells = grid_layout_input_data(layout, local_context, &repeater_steps);
89 let repeater_indices = grid_repeater_indices(layout, local_context, &repeater_steps);
90 if let Some(buttons_roles) = &layout.dialog_button_roles {
91 let roles = buttons_roles
92 .iter()
93 .map(|r| DialogButtonRole::from_str(r).unwrap())
94 .collect::<Vec<_>>();
95 core_layout::organize_dialog_button_layout(
96 Slice::from_slice(cells.as_slice()),
97 Slice::from_slice(roles.as_slice()),
98 )
99 .into()
100 } else {
101 core_layout::organize_grid_layout(
102 Slice::from_slice(cells.as_slice()),
103 Slice::from_slice(repeater_indices.as_slice()),
104 Slice::from_slice(repeater_steps.as_slice()),
105 )
106 .into()
107 }
108}
109
110pub(crate) fn solve_grid_layout(
111 organized_data: &GridLayoutOrganizedData,
112 grid_layout: &GridLayout,
113 orientation: Orientation,
114 local_context: &mut EvalLocalContext,
115) -> Value {
116 let component = local_context.component_instance;
117 let expr_eval = |nr: &NamedReference| -> f32 {
118 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
119 };
120 let repeater_steps = grid_repeater_steps(grid_layout, local_context);
121 let repeater_indices = grid_repeater_indices(grid_layout, local_context, &repeater_steps);
122 let constraints =
123 grid_layout_constraints(grid_layout, orientation, local_context, &repeater_steps);
124
125 let (padding, spacing) = padding_and_spacing(&grid_layout.geometry, orientation, &expr_eval);
126 let size_ref = grid_layout.geometry.rect.size_reference(orientation);
127
128 let data = core_layout::GridLayoutData {
129 size: size_ref.map(expr_eval).unwrap_or(0.),
130 spacing,
131 padding,
132 organized_data: organized_data.clone(),
133 };
134
135 core_layout::solve_grid_layout(
136 &data,
137 Slice::from_slice(constraints.as_slice()),
138 to_runtime(orientation),
139 Slice::from_slice(repeater_indices.as_slice()),
140 Slice::from_slice(repeater_steps.as_slice()),
141 )
142 .into()
143}
144
145pub(crate) fn solve_box_layout(
146 box_layout: &BoxLayout,
147 orientation: Orientation,
148 local_context: &mut EvalLocalContext,
149) -> Value {
150 let component = local_context.component_instance;
151 let expr_eval = |nr: &NamedReference| -> f32 {
152 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
153 };
154
155 let mut repeated_indices = Vec::new();
156 let (cells, alignment) = box_layout_data(
157 box_layout,
158 orientation,
159 component,
160 &expr_eval,
161 Some(&mut repeated_indices),
162 );
163 let (padding, spacing) = padding_and_spacing(&box_layout.geometry, orientation, &expr_eval);
164 let size = box_layout.geometry.rect.size_reference(orientation).map(&expr_eval).unwrap_or(0.);
165 if orientation == box_layout.orientation {
166 core_layout::solve_box_layout(
167 &core_layout::BoxLayoutData {
168 size,
169 spacing,
170 padding,
171 alignment,
172 cells: Slice::from(cells.as_slice()),
173 },
174 Slice::from(repeated_indices.as_slice()),
175 )
176 .into()
177 } else {
178 let align_items = box_layout
179 .cross_alignment
180 .as_ref()
181 .map(|nr| {
182 eval::load_property(component, &nr.element(), nr.name())
183 .unwrap()
184 .try_into()
185 .unwrap_or_default()
186 })
187 .unwrap_or_default();
188 core_layout::solve_box_layout_ortho(
189 &core_layout::BoxLayoutOrthoData {
190 size,
191 padding,
192 align_items,
193 cells: Slice::from(cells.as_slice()),
194 },
195 Slice::from(repeated_indices.as_slice()),
196 )
197 .into()
198 }
199}
200
201pub(crate) fn solve_flexbox_layout(
202 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
203 local_context: &mut EvalLocalContext,
204) -> Value {
205 let component = local_context.component_instance;
206 let expr_eval = |nr: &NamedReference| -> f32 {
207 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
208 };
209
210 let width_ref = &flexbox_layout.geometry.rect.width_reference;
211 let height_ref = &flexbox_layout.geometry.rect.height_reference;
212 let direction = flexbox_layout_direction(flexbox_layout, local_context);
213
214 let container_width_for_cells = match direction {
217 i_slint_core::items::FlexboxLayoutDirection::Column
218 | i_slint_core::items::FlexboxLayoutDirection::ColumnReverse => {
219 width_ref.as_ref().map(&expr_eval)
220 }
221 _ => None,
222 };
223
224 let (cells_h, cells_v, repeated_indices) = flexbox_layout_data(
225 flexbox_layout,
226 component,
227 &expr_eval,
228 local_context,
229 container_width_for_cells,
230 );
231
232 let alignment = flexbox_layout
233 .geometry
234 .alignment
235 .as_ref()
236 .map_or(i_slint_core::items::LayoutAlignment::default(), |nr| {
237 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
238 });
239 let align_content = flexbox_layout
240 .align_content
241 .as_ref()
242 .map_or(i_slint_core::items::FlexboxLayoutAlignContent::default(), |nr| {
243 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
244 });
245 let align_items = flexbox_layout
246 .align_items
247 .as_ref()
248 .map_or(i_slint_core::items::LayoutAlignItems::default(), |nr| {
249 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
250 });
251 let flex_wrap = flexbox_layout
252 .flex_wrap
253 .as_ref()
254 .map_or(i_slint_core::items::FlexboxLayoutWrap::default(), |nr| {
255 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
256 });
257
258 let (padding_h, spacing_h) =
259 padding_and_spacing(&flexbox_layout.geometry, Orientation::Horizontal, &expr_eval);
260 let (padding_v, spacing_v) =
261 padding_and_spacing(&flexbox_layout.geometry, Orientation::Vertical, &expr_eval);
262
263 let data = core_layout::FlexboxLayoutData {
264 width: width_ref.as_ref().map(&expr_eval).unwrap_or(0.),
265 height: height_ref.as_ref().map(&expr_eval).unwrap_or(0.),
266 spacing_h,
267 spacing_v,
268 padding_h,
269 padding_v,
270 alignment,
271 direction,
272 align_content,
273 align_items,
274 flex_wrap,
275 cells_h: Slice::from(cells_h.as_slice()),
276 cells_v: Slice::from(cells_v.as_slice()),
277 };
278 let ri = Slice::from(repeated_indices.as_slice());
279
280 let window_adapter = component.window_adapter();
282 let mut child_elem_ids: Vec<Option<smol_str::SmolStr>> = Vec::new();
283 for layout_elem in &flexbox_layout.elems {
284 if layout_elem.item.element.borrow().repeated.is_some() {
285 let component_vec = repeater_instances(component, &layout_elem.item.element);
286 for _ in 0..component_vec.len() {
287 child_elem_ids.push(None);
288 }
289 } else {
290 child_elem_ids.push(Some(layout_elem.item.element.borrow().id.clone()));
291 }
292 }
293
294 let mut measure = |child_index: usize,
299 known_w: Option<f32>,
300 known_h: Option<f32>|
301 -> (f32, f32) {
302 let default_w = cells_h.get(child_index).map_or(0., |c| c.constraint.preferred_bounded());
303 let default_h = cells_v.get(child_index).map_or(0., |c| c.constraint.preferred_bounded());
304 let w = known_w.unwrap_or(default_w);
305 let h = known_h.unwrap_or(default_h);
306
307 let elem_id = match child_elem_ids.get(child_index) {
308 Some(Some(id)) => id,
309 _ => return (w, h),
310 };
311 let item_within = match component.description.items.get(elem_id.as_str()) {
312 Some(i) => i,
313 None => return (w, h),
314 };
315
316 if known_w.is_some() && known_h.is_none() {
318 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
319 let item_rc = ItemRc::new(vtable::VRc::into_dyn(item_comp), item_within.item_index());
320 let item = unsafe { item_within.item_from_item_tree(component.as_ptr()) };
321 let v_info = item.as_ref().layout_info(
322 to_runtime(Orientation::Vertical),
323 w,
324 &window_adapter,
325 &item_rc,
326 );
327 return (w, v_info.preferred_bounded());
328 }
329 if known_h.is_some() && known_w.is_none() {
330 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
331 let item_rc = ItemRc::new(vtable::VRc::into_dyn(item_comp), item_within.item_index());
332 let item = unsafe { item_within.item_from_item_tree(component.as_ptr()) };
333 let h_info = item.as_ref().layout_info(
334 to_runtime(Orientation::Horizontal),
335 h,
336 &window_adapter,
337 &item_rc,
338 );
339 return (h_info.preferred_bounded(), h);
340 }
341 (w, h)
342 };
343
344 core_layout::solve_flexbox_layout_with_measure(&data, ri, Some(&mut measure)).into()
345}
346
347fn flexbox_layout_direction(
348 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
349 local_context: &EvalLocalContext,
350) -> FlexboxLayoutDirection {
351 flexbox_layout
352 .direction
353 .as_ref()
354 .and_then(|nr| {
355 let value =
356 eval::load_property(local_context.component_instance, &nr.element(), nr.name())
357 .ok()?;
358 if let Value::EnumerationValue(_, variant) = &value {
359 match variant.as_str() {
360 "row" => Some(FlexboxLayoutDirection::Row),
361 "row-reverse" => Some(FlexboxLayoutDirection::RowReverse),
362 "column" => Some(FlexboxLayoutDirection::Column),
363 "column-reverse" => Some(FlexboxLayoutDirection::ColumnReverse),
364 _ => None,
365 }
366 } else {
367 None
368 }
369 })
370 .unwrap_or(FlexboxLayoutDirection::Row)
371}
372
373pub(crate) fn compute_flexbox_layout_info(
374 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
375 orientation: Orientation,
376 local_context: &mut EvalLocalContext,
377) -> Value {
378 let component = local_context.component_instance;
379 let expr_eval = |nr: &NamedReference| -> f32 {
380 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
381 };
382
383 let (cells_h, cells_v, _repeated_indices) =
384 flexbox_layout_data(flexbox_layout, component, &expr_eval, local_context, None);
385
386 let direction = flexbox_layout_direction(flexbox_layout, local_context);
388
389 let is_main_axis = matches!(
391 (direction, orientation),
392 (FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse, Orientation::Horizontal)
393 | (
394 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse,
395 Orientation::Vertical
396 )
397 );
398
399 let (padding_h, spacing_h) =
400 padding_and_spacing(&flexbox_layout.geometry, Orientation::Horizontal, &expr_eval);
401 let (padding_v, spacing_v) =
402 padding_and_spacing(&flexbox_layout.geometry, Orientation::Vertical, &expr_eval);
403
404 let flex_wrap = flexbox_layout
405 .flex_wrap
406 .as_ref()
407 .map_or(i_slint_core::items::FlexboxLayoutWrap::default(), |nr| {
408 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
409 });
410
411 if is_main_axis {
412 let (cells, spacing, padding) = match orientation {
413 Orientation::Horizontal => (&cells_h, spacing_h, &padding_h),
414 Orientation::Vertical => (&cells_v, spacing_v, &padding_v),
415 };
416 core_layout::flexbox_layout_info_main_axis(
417 Slice::from(cells.as_slice()),
418 spacing,
419 padding,
420 flex_wrap,
421 )
422 .into()
423 } else {
424 let constraint_size = match orientation {
428 Orientation::Horizontal => {
429 let height_ref = &flexbox_layout.geometry.rect.height_reference;
430 height_ref.as_ref().map(&expr_eval).unwrap_or(0.)
431 }
432 Orientation::Vertical => {
433 let width_ref = &flexbox_layout.geometry.rect.width_reference;
434 width_ref.as_ref().map(&expr_eval).unwrap_or(0.)
435 }
436 };
437 core_layout::flexbox_layout_info_cross_axis(
438 Slice::from(cells_h.as_slice()),
439 Slice::from(cells_v.as_slice()),
440 spacing_h,
441 spacing_v,
442 &padding_h,
443 &padding_v,
444 direction,
445 flex_wrap,
446 constraint_size,
447 )
448 .into()
449 }
450}
451
452fn flexbox_layout_data(
453 flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
454 component: InstanceRef,
455 expr_eval: &impl Fn(&NamedReference) -> f32,
456 _local_context: &mut EvalLocalContext,
457 container_width: Option<f32>,
458) -> (Vec<core_layout::FlexboxLayoutItemInfo>, Vec<core_layout::FlexboxLayoutItemInfo>, Vec<u32>) {
459 let window_adapter = component.window_adapter();
460 let mut cells_h = Vec::with_capacity(flexbox_layout.elems.len());
461 let mut cells_v = Vec::with_capacity(flexbox_layout.elems.len());
462 let mut repeated_indices = Vec::new();
463
464 struct ChildInfo {
467 flex_grow: f32,
468 flex_shrink: f32,
469 flex_basis: f32,
470 flex_align_self: i_slint_core::items::FlexboxLayoutAlignSelf,
471 flex_order: i32,
472 }
473 let mut static_children: Vec<Option<ChildInfo>> = Vec::new(); for layout_elem in &flexbox_layout.elems {
476 if layout_elem.item.element.borrow().repeated.is_some() {
477 let component_vec = repeater_instances(component, &layout_elem.item.element);
478 repeated_indices.push(cells_h.len() as u32);
479 repeated_indices.push(component_vec.len() as u32);
480 cells_h.extend(component_vec.iter().map(|x| {
481 x.as_pin_ref().flexbox_layout_item_info(to_runtime(Orientation::Horizontal), None)
482 }));
483 cells_v.extend(component_vec.iter().map(|x| {
484 x.as_pin_ref().flexbox_layout_item_info(to_runtime(Orientation::Vertical), None)
485 }));
486 for _ in 0..component_vec.len() {
487 static_children.push(None);
488 }
489 } else {
490 let mut layout_info_h = get_layout_info(
491 &layout_elem.item.element,
492 component,
493 &window_adapter,
494 Orientation::Horizontal,
495 );
496 fill_layout_info_constraints(
497 &mut layout_info_h,
498 &layout_elem.item.constraints,
499 Orientation::Horizontal,
500 expr_eval,
501 );
502 let flex_grow = layout_elem.flex_grow.as_ref().map(&expr_eval).unwrap_or(0.0);
506 let flex_shrink = layout_elem.flex_shrink.as_ref().map(&expr_eval).unwrap_or(1.0);
507 let flex_basis = layout_elem.flex_basis.as_ref().map(&expr_eval).unwrap_or(-1.0);
508 let align_self = layout_elem
509 .align_self
510 .as_ref()
511 .map(|nr| {
512 eval::load_property(component, &nr.element(), nr.name())
513 .unwrap()
514 .try_into()
515 .unwrap()
516 })
517 .unwrap_or(i_slint_core::items::FlexboxLayoutAlignSelf::default());
518 let order = layout_elem.order.as_ref().map(expr_eval).unwrap_or(0.0) as i32;
519 cells_h.push(core_layout::FlexboxLayoutItemInfo {
520 constraint: layout_info_h,
521 flex_grow,
522 flex_shrink,
523 flex_basis,
524 flex_align_self: align_self,
525 flex_order: order,
526 });
527 cells_v.push(core_layout::FlexboxLayoutItemInfo::default());
529 static_children.push(Some(ChildInfo {
530 flex_grow,
531 flex_shrink,
532 flex_basis,
533 flex_align_self: align_self,
534 flex_order: order,
535 }));
536 }
537 }
538
539 let mut cell_idx = 0usize;
543 for layout_elem in &flexbox_layout.elems {
544 if layout_elem.item.element.borrow().repeated.is_some() {
545 let component_vec = repeater_instances(component, &layout_elem.item.element);
546 cell_idx += component_vec.len();
547 } else {
549 let width_constraint =
550 container_width.unwrap_or_else(|| cells_h[cell_idx].constraint.preferred_bounded());
551 let mut layout_info_v = get_layout_info_with_constraint(
552 &layout_elem.item.element,
553 component,
554 &window_adapter,
555 Orientation::Vertical,
556 width_constraint,
557 );
558 fill_layout_info_constraints(
559 &mut layout_info_v,
560 &layout_elem.item.constraints,
561 Orientation::Vertical,
562 expr_eval,
563 );
564 if let Some(info) = &static_children[cell_idx] {
565 cells_v[cell_idx] = core_layout::FlexboxLayoutItemInfo {
566 constraint: layout_info_v,
567 flex_grow: info.flex_grow,
568 flex_shrink: info.flex_shrink,
569 flex_basis: info.flex_basis,
570 flex_align_self: info.flex_align_self,
571 flex_order: info.flex_order,
572 };
573 }
574 cell_idx += 1;
575 }
576 }
577
578 (cells_h, cells_v, repeated_indices)
579}
580
581fn padding_and_spacing(
583 layout_geometry: &LayoutGeometry,
584 orientation: Orientation,
585 expr_eval: &impl Fn(&NamedReference) -> f32,
586) -> (core_layout::Padding, f32) {
587 let spacing = layout_geometry.spacing.orientation(orientation).map_or(0., expr_eval);
588 let (begin, end) = layout_geometry.padding.begin_end(orientation);
589 let padding =
590 core_layout::Padding { begin: begin.map_or(0., expr_eval), end: end.map_or(0., expr_eval) };
591 (padding, spacing)
592}
593
594fn repeater_instances(
595 component: InstanceRef,
596 elem: &ElementRc,
597) -> Vec<crate::dynamic_item_tree::DynamicComponentVRc> {
598 generativity::make_guard!(guard);
599 let rep =
600 crate::dynamic_item_tree::get_repeater_by_name(component, elem.borrow().id.as_str(), guard);
601 rep.0.as_ref().track_instance_changes();
602 rep.0.as_ref().instances_vec()
603}
604
605fn grid_layout_input_data(
606 grid_layout: &i_slint_compiler::layout::GridLayout,
607 ctx: &EvalLocalContext,
608 repeater_steps: &[u32],
609) -> Vec<GridLayoutInputData> {
610 let component = ctx.component_instance;
611 let mut result = Vec::with_capacity(grid_layout.elems.len());
612 let mut after_repeater_in_same_row = false;
613 let mut new_row = true;
614 let mut repeater_idx = 0usize;
615 for elem in grid_layout.elems.iter() {
616 let eval_or_default = |expr: &RowColExpr, component: InstanceRef| match expr {
617 RowColExpr::Literal(value) => *value as f32,
618 RowColExpr::Auto => i_slint_common::ROW_COL_AUTO,
619 RowColExpr::Named(nr) => {
620 eval::load_property(component, &nr.element(), nr.name())
622 .unwrap()
623 .try_into()
624 .unwrap()
625 }
626 };
627
628 let cell_new_row = elem.cell.borrow().new_row;
629 if cell_new_row {
630 after_repeater_in_same_row = false;
631 }
632 if elem.item.element.borrow().repeated.is_some() {
633 let component_vec = repeater_instances(component, &elem.item.element);
634 new_row = cell_new_row;
635 for erased_sub_comp in &component_vec {
636 generativity::make_guard!(guard);
638 let sub_comp = erased_sub_comp.as_pin_ref();
639 let sub_instance_ref =
640 unsafe { InstanceRef::from_pin_ref(sub_comp.borrow(), guard) };
641
642 if let Some(children) = elem.cell.borrow().child_items.as_ref() {
643 new_row = true;
645 let start_count = result.len();
646
647 for child_template in children {
653 match child_template {
654 i_slint_compiler::layout::RowChildTemplate::Static(child_item) => {
655 let (row_val, col_val, rowspan_val, colspan_val) = {
656 let element_ref = child_item.element.borrow();
657 let child_cell =
658 element_ref.grid_layout_cell.as_ref().unwrap().borrow();
659 (
660 eval_or_default(&child_cell.row_expr, sub_instance_ref),
661 eval_or_default(&child_cell.col_expr, sub_instance_ref),
662 eval_or_default(&child_cell.rowspan_expr, sub_instance_ref),
663 eval_or_default(&child_cell.colspan_expr, sub_instance_ref),
664 )
665 };
666 result.push(GridLayoutInputData {
667 new_row,
668 col: col_val,
669 row: row_val,
670 colspan: colspan_val,
671 rowspan: rowspan_val,
672 });
673 new_row = false;
674 }
675 i_slint_compiler::layout::RowChildTemplate::Repeated {
676 repeated_element,
677 ..
678 } => {
679 let inner_instances =
680 repeater_instances(sub_instance_ref, repeated_element);
681 for i in 0..inner_instances.len() {
682 result.push(GridLayoutInputData {
683 new_row: i == 0 && new_row,
684 ..Default::default()
685 });
686 }
687 if !inner_instances.is_empty() {
688 new_row = false;
689 }
690 }
691 }
692 }
693 let cells_pushed = result.len() - start_count;
695 let expected_step =
696 repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
697 for _ in cells_pushed..expected_step {
698 result.push(GridLayoutInputData::default());
699 }
700 } else {
701 let cell = elem.cell.borrow();
703 let row = eval_or_default(&cell.row_expr, sub_instance_ref);
704 let col = eval_or_default(&cell.col_expr, sub_instance_ref);
705 let rowspan = eval_or_default(&cell.rowspan_expr, sub_instance_ref);
706 let colspan = eval_or_default(&cell.colspan_expr, sub_instance_ref);
707 result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
708 new_row = false;
709 }
710 }
711 repeater_idx += 1;
712 after_repeater_in_same_row = true;
713 } else {
714 let new_row =
715 if cell_new_row || !after_repeater_in_same_row { cell_new_row } else { new_row };
716 let row = eval_or_default(&elem.cell.borrow().row_expr, component);
717 let col = eval_or_default(&elem.cell.borrow().col_expr, component);
718 let rowspan = eval_or_default(&elem.cell.borrow().rowspan_expr, component);
719 let colspan = eval_or_default(&elem.cell.borrow().colspan_expr, component);
720 result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
721 }
722 }
723 result
724}
725
726fn row_runtime_child_count(
730 child_items: &[i_slint_compiler::layout::RowChildTemplate],
731 sub_instance_ref: InstanceRef,
732) -> usize {
733 let mut count = 0;
734 for child in child_items {
735 if let Some(repeated_element) = child.repeated_element() {
736 count += repeater_instances(sub_instance_ref, repeated_element).len();
737 } else {
738 count += 1;
739 }
740 }
741 count
742}
743
744fn grid_repeater_indices(
745 grid_layout: &i_slint_compiler::layout::GridLayout,
746 ctx: &mut EvalLocalContext,
747 repeater_steps: &[u32],
748) -> Vec<u32> {
749 let component = ctx.component_instance;
750 let mut repeater_indices = Vec::new();
751 let mut num_cells = 0;
752 let mut step_idx = 0;
753 for elem in grid_layout.elems.iter() {
754 if elem.item.element.borrow().repeated.is_some() {
755 let component_vec = repeater_instances(component, &elem.item.element);
756 repeater_indices.push(num_cells as _);
757 repeater_indices.push(component_vec.len() as _);
758 let item_count = repeater_steps[step_idx] as usize;
759 num_cells += component_vec.len() * item_count;
760 step_idx += 1;
761 } else {
762 num_cells += 1;
763 }
764 }
765 repeater_indices
766}
767
768fn grid_repeater_steps(
769 grid_layout: &i_slint_compiler::layout::GridLayout,
770 ctx: &mut EvalLocalContext,
771) -> Vec<u32> {
772 let component = ctx.component_instance;
773 let mut repeater_steps = Vec::new();
774 for elem in grid_layout.elems.iter() {
775 if elem.item.element.borrow().repeated.is_some() {
776 let item_count = match &elem.cell.borrow().child_items {
777 Some(ci)
778 if ci.iter().any(i_slint_compiler::layout::RowChildTemplate::is_repeated) =>
779 {
780 let component_vec = repeater_instances(component, &elem.item.element);
782 component_vec
783 .iter()
784 .map(|sub| {
785 generativity::make_guard!(guard);
786 let sub_pin = sub.as_pin_ref();
787 let sub_ref =
788 unsafe { InstanceRef::from_pin_ref(sub_pin.borrow(), guard) };
789 row_runtime_child_count(ci, sub_ref)
790 })
791 .max()
792 .unwrap_or(0)
793 }
794 Some(ci) => ci.len(),
795 None => 1,
796 };
797 repeater_steps.push(item_count as u32);
798 }
799 }
800 repeater_steps
801}
802
803fn grid_layout_constraints(
804 grid_layout: &i_slint_compiler::layout::GridLayout,
805 orientation: Orientation,
806 ctx: &mut EvalLocalContext,
807 repeater_steps: &[u32],
808) -> Vec<core_layout::LayoutItemInfo> {
809 let component = ctx.component_instance;
810 let expr_eval = |nr: &NamedReference| -> f32 {
811 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
812 };
813 let mut constraints = Vec::with_capacity(grid_layout.elems.len());
814
815 let mut repeater_idx = 0usize;
816 for layout_elem in grid_layout.elems.iter() {
817 if layout_elem.item.element.borrow().repeated.is_some() {
818 let component_vec = repeater_instances(component, &layout_elem.item.element);
819 let child_items = layout_elem.cell.borrow().child_items.clone();
820 let has_children = child_items.is_some();
821 if has_children {
822 let ci = child_items.as_ref().unwrap();
824 let step = repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
825 for sub_comp in &component_vec {
826 let per_instance_start = constraints.len();
827 generativity::make_guard!(guard);
829 let sub_pin = sub_comp.as_pin_ref();
830 let sub_borrow = sub_pin.borrow();
831 let sub_instance_ref = unsafe { InstanceRef::from_pin_ref(sub_borrow, guard) };
832 let expr_eval = |nr: &NamedReference| -> f32 {
833 eval::load_property(sub_instance_ref, &nr.element(), nr.name())
834 .unwrap()
835 .try_into()
836 .unwrap()
837 };
838
839 for child_template in ci.iter() {
843 match child_template {
844 i_slint_compiler::layout::RowChildTemplate::Static(child_item) => {
845 let mut layout_info = crate::eval_layout::get_layout_info(
846 &child_item.element,
847 sub_instance_ref,
848 &sub_instance_ref.window_adapter(),
849 orientation,
850 );
851 fill_layout_info_constraints(
852 &mut layout_info,
853 &child_item.constraints,
854 orientation,
855 &expr_eval,
856 );
857 constraints
858 .push(core_layout::LayoutItemInfo { constraint: layout_info });
859 }
860 i_slint_compiler::layout::RowChildTemplate::Repeated {
861 item: child_item,
862 repeated_element,
863 } => {
864 let inner_instances =
866 repeater_instances(sub_instance_ref, repeated_element);
867 for inner_comp in &inner_instances {
868 let inner_pin = inner_comp.as_pin_ref();
869 let mut layout_info =
870 inner_pin.layout_item_info(to_runtime(orientation), None);
871 generativity::make_guard!(inner_guard);
874 let inner_borrow = inner_pin.borrow();
875 let inner_instance_ref = unsafe {
876 InstanceRef::from_pin_ref(inner_borrow, inner_guard)
877 };
878 let inner_expr_eval = |nr: &NamedReference| -> f32 {
879 eval::load_property(
880 inner_instance_ref,
881 &nr.element(),
882 nr.name(),
883 )
884 .unwrap()
885 .try_into()
886 .unwrap()
887 };
888 fill_layout_info_constraints(
889 &mut layout_info.constraint,
890 &child_item.constraints,
891 orientation,
892 &inner_expr_eval,
893 );
894 constraints.push(layout_info);
895 }
896 }
897 }
898 }
899 let pushed = constraints.len() - per_instance_start;
902 for _ in pushed..step {
903 constraints.push(core_layout::LayoutItemInfo::default());
904 }
905 }
906 } else {
907 constraints.extend(
909 component_vec
910 .iter()
911 .map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
912 );
913 }
914 repeater_idx += 1;
915 } else {
916 let mut layout_info = get_layout_info(
917 &layout_elem.item.element,
918 component,
919 &component.window_adapter(),
920 orientation,
921 );
922 fill_layout_info_constraints(
923 &mut layout_info,
924 &layout_elem.item.constraints,
925 orientation,
926 &expr_eval,
927 );
928 constraints.push(core_layout::LayoutItemInfo { constraint: layout_info });
929 }
930 }
931 constraints
932}
933
934fn box_layout_data(
936 box_layout: &i_slint_compiler::layout::BoxLayout,
937 orientation: Orientation,
938 component: InstanceRef,
939 expr_eval: &impl Fn(&NamedReference) -> f32,
940 mut repeater_indices: Option<&mut Vec<u32>>,
941) -> (Vec<core_layout::LayoutItemInfo>, i_slint_core::items::LayoutAlignment) {
942 let window_adapter = component.window_adapter();
943 let mut cells = Vec::with_capacity(box_layout.elems.len());
944 for cell in &box_layout.elems {
945 if cell.element.borrow().repeated.is_some() {
946 let component_vec = repeater_instances(component, &cell.element);
948 if let Some(ri) = repeater_indices.as_mut() {
949 ri.push(cells.len() as _);
950 ri.push(component_vec.len() as _);
951 }
952 cells.extend(
953 component_vec
954 .iter()
955 .map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
956 );
957 } else {
958 let mut layout_info =
960 get_layout_info(&cell.element, component, &window_adapter, orientation);
961 fill_layout_info_constraints(
962 &mut layout_info,
963 &cell.constraints,
964 orientation,
965 &expr_eval,
966 );
967 cells.push(core_layout::LayoutItemInfo { constraint: layout_info });
968 }
969 }
970 let alignment = box_layout
971 .geometry
972 .alignment
973 .as_ref()
974 .map(|nr| {
975 eval::load_property(component, &nr.element(), nr.name())
976 .unwrap()
977 .try_into()
978 .unwrap_or_default()
979 })
980 .unwrap_or_default();
981 (cells, alignment)
982}
983
984pub(crate) fn fill_layout_info_constraints(
985 layout_info: &mut core_layout::LayoutInfo,
986 constraints: &LayoutConstraints,
987 orientation: Orientation,
988 expr_eval: &impl Fn(&NamedReference) -> f32,
989) {
990 let is_percent =
991 |nr: &NamedReference| Expression::PropertyReference(nr.clone()).ty() == Type::Percent;
992
993 match orientation {
994 Orientation::Horizontal => {
995 if let Some(e) = constraints.min_width.as_ref() {
996 if !is_percent(e) {
997 layout_info.min = expr_eval(e)
998 } else {
999 layout_info.min_percent = expr_eval(e)
1000 }
1001 }
1002 if let Some(e) = constraints.max_width.as_ref() {
1003 if !is_percent(e) {
1004 layout_info.max = expr_eval(e)
1005 } else {
1006 layout_info.max_percent = expr_eval(e)
1007 }
1008 }
1009 if let Some(e) = constraints.preferred_width.as_ref() {
1010 layout_info.preferred = expr_eval(e);
1011 }
1012 if let Some(e) = constraints.horizontal_stretch.as_ref() {
1013 layout_info.stretch = expr_eval(e);
1014 }
1015 }
1016 Orientation::Vertical => {
1017 if let Some(e) = constraints.min_height.as_ref() {
1018 if !is_percent(e) {
1019 layout_info.min = expr_eval(e)
1020 } else {
1021 layout_info.min_percent = expr_eval(e)
1022 }
1023 }
1024 if let Some(e) = constraints.max_height.as_ref() {
1025 if !is_percent(e) {
1026 layout_info.max = expr_eval(e)
1027 } else {
1028 layout_info.max_percent = expr_eval(e)
1029 }
1030 }
1031 if let Some(e) = constraints.preferred_height.as_ref() {
1032 layout_info.preferred = expr_eval(e);
1033 }
1034 if let Some(e) = constraints.vertical_stretch.as_ref() {
1035 layout_info.stretch = expr_eval(e);
1036 }
1037 }
1038 }
1039}
1040
1041pub(crate) fn get_layout_info(
1043 elem: &ElementRc,
1044 component: InstanceRef,
1045 window_adapter: &Rc<dyn WindowAdapter>,
1046 orientation: Orientation,
1047) -> core_layout::LayoutInfo {
1048 get_layout_info_with_constraint(elem, component, window_adapter, orientation, -1.)
1049}
1050
1051fn get_layout_info_with_constraint(
1052 elem: &ElementRc,
1053 component: InstanceRef,
1054 window_adapter: &Rc<dyn WindowAdapter>,
1055 orientation: Orientation,
1056 cross_axis_constraint: f32,
1057) -> core_layout::LayoutInfo {
1058 let elem = elem.borrow();
1059 if let Some(nr) = elem.layout_info_prop(orientation) {
1060 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
1061 } else {
1062 let item = &component
1063 .description
1064 .items
1065 .get(elem.id.as_str())
1066 .unwrap_or_else(|| panic!("Internal error: Item {} not found", elem.id));
1067 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
1068
1069 unsafe {
1070 item.item_from_item_tree(component.as_ptr()).as_ref().layout_info(
1071 to_runtime(orientation),
1072 cross_axis_constraint,
1073 window_adapter,
1074 &ItemRc::new(vtable::VRc::into_dyn(item_comp), item.item_index()),
1075 )
1076 }
1077 }
1078}