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