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 let extra_data = component.description.extra_data_offset.apply(component.as_ref());
602 rep.0.as_ref().ensure_updated(|| {
603 crate::dynamic_item_tree::instantiate(
604 rep.1.clone(),
605 component.self_weak().get().cloned(),
606 None,
607 None,
608 extra_data.globals.get().unwrap().clone(),
609 )
610 });
611 rep.0.as_ref().instances_vec()
612}
613
614fn grid_layout_input_data(
615 grid_layout: &i_slint_compiler::layout::GridLayout,
616 ctx: &EvalLocalContext,
617 repeater_steps: &[u32],
618) -> Vec<GridLayoutInputData> {
619 let component = ctx.component_instance;
620 let mut result = Vec::with_capacity(grid_layout.elems.len());
621 let mut after_repeater_in_same_row = false;
622 let mut new_row = true;
623 let mut repeater_idx = 0usize;
624 for elem in grid_layout.elems.iter() {
625 let eval_or_default = |expr: &RowColExpr, component: InstanceRef| match expr {
626 RowColExpr::Literal(value) => *value as f32,
627 RowColExpr::Auto => i_slint_common::ROW_COL_AUTO,
628 RowColExpr::Named(nr) => {
629 eval::load_property(component, &nr.element(), nr.name())
631 .unwrap()
632 .try_into()
633 .unwrap()
634 }
635 };
636
637 let cell_new_row = elem.cell.borrow().new_row;
638 if cell_new_row {
639 after_repeater_in_same_row = false;
640 }
641 if elem.item.element.borrow().repeated.is_some() {
642 let component_vec = repeater_instances(component, &elem.item.element);
643 new_row = cell_new_row;
644 for erased_sub_comp in &component_vec {
645 generativity::make_guard!(guard);
647 let sub_comp = erased_sub_comp.as_pin_ref();
648 let sub_instance_ref =
649 unsafe { InstanceRef::from_pin_ref(sub_comp.borrow(), guard) };
650
651 if let Some(children) = elem.cell.borrow().child_items.as_ref() {
652 new_row = true;
654 let start_count = result.len();
655
656 for child_template in children {
662 match child_template {
663 i_slint_compiler::layout::RowChildTemplate::Static(child_item) => {
664 let (row_val, col_val, rowspan_val, colspan_val) = {
665 let element_ref = child_item.element.borrow();
666 let child_cell =
667 element_ref.grid_layout_cell.as_ref().unwrap().borrow();
668 (
669 eval_or_default(&child_cell.row_expr, sub_instance_ref),
670 eval_or_default(&child_cell.col_expr, sub_instance_ref),
671 eval_or_default(&child_cell.rowspan_expr, sub_instance_ref),
672 eval_or_default(&child_cell.colspan_expr, sub_instance_ref),
673 )
674 };
675 result.push(GridLayoutInputData {
676 new_row,
677 col: col_val,
678 row: row_val,
679 colspan: colspan_val,
680 rowspan: rowspan_val,
681 });
682 new_row = false;
683 }
684 i_slint_compiler::layout::RowChildTemplate::Repeated {
685 repeated_element,
686 ..
687 } => {
688 let inner_instances =
689 repeater_instances(sub_instance_ref, repeated_element);
690 for i in 0..inner_instances.len() {
691 result.push(GridLayoutInputData {
692 new_row: i == 0 && new_row,
693 ..Default::default()
694 });
695 }
696 if !inner_instances.is_empty() {
697 new_row = false;
698 }
699 }
700 }
701 }
702 let cells_pushed = result.len() - start_count;
704 let expected_step =
705 repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
706 for _ in cells_pushed..expected_step {
707 result.push(GridLayoutInputData::default());
708 }
709 } else {
710 let cell = elem.cell.borrow();
712 let row = eval_or_default(&cell.row_expr, sub_instance_ref);
713 let col = eval_or_default(&cell.col_expr, sub_instance_ref);
714 let rowspan = eval_or_default(&cell.rowspan_expr, sub_instance_ref);
715 let colspan = eval_or_default(&cell.colspan_expr, sub_instance_ref);
716 result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
717 new_row = false;
718 }
719 }
720 repeater_idx += 1;
721 after_repeater_in_same_row = true;
722 } else {
723 let new_row =
724 if cell_new_row || !after_repeater_in_same_row { cell_new_row } else { new_row };
725 let row = eval_or_default(&elem.cell.borrow().row_expr, component);
726 let col = eval_or_default(&elem.cell.borrow().col_expr, component);
727 let rowspan = eval_or_default(&elem.cell.borrow().rowspan_expr, component);
728 let colspan = eval_or_default(&elem.cell.borrow().colspan_expr, component);
729 result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
730 }
731 }
732 result
733}
734
735fn row_runtime_child_count(
739 child_items: &[i_slint_compiler::layout::RowChildTemplate],
740 sub_instance_ref: InstanceRef,
741) -> usize {
742 let mut count = 0;
743 for child in child_items {
744 if let Some(repeated_element) = child.repeated_element() {
745 count += repeater_instances(sub_instance_ref, repeated_element).len();
746 } else {
747 count += 1;
748 }
749 }
750 count
751}
752
753fn grid_repeater_indices(
754 grid_layout: &i_slint_compiler::layout::GridLayout,
755 ctx: &mut EvalLocalContext,
756 repeater_steps: &[u32],
757) -> Vec<u32> {
758 let component = ctx.component_instance;
759 let mut repeater_indices = Vec::new();
760 let mut num_cells = 0;
761 let mut step_idx = 0;
762 for elem in grid_layout.elems.iter() {
763 if elem.item.element.borrow().repeated.is_some() {
764 let component_vec = repeater_instances(component, &elem.item.element);
765 repeater_indices.push(num_cells as _);
766 repeater_indices.push(component_vec.len() as _);
767 let item_count = repeater_steps[step_idx] as usize;
768 num_cells += component_vec.len() * item_count;
769 step_idx += 1;
770 } else {
771 num_cells += 1;
772 }
773 }
774 repeater_indices
775}
776
777fn grid_repeater_steps(
778 grid_layout: &i_slint_compiler::layout::GridLayout,
779 ctx: &mut EvalLocalContext,
780) -> Vec<u32> {
781 let component = ctx.component_instance;
782 let mut repeater_steps = Vec::new();
783 for elem in grid_layout.elems.iter() {
784 if elem.item.element.borrow().repeated.is_some() {
785 let item_count = match &elem.cell.borrow().child_items {
786 Some(ci)
787 if ci.iter().any(i_slint_compiler::layout::RowChildTemplate::is_repeated) =>
788 {
789 let component_vec = repeater_instances(component, &elem.item.element);
791 component_vec
792 .iter()
793 .map(|sub| {
794 generativity::make_guard!(guard);
795 let sub_pin = sub.as_pin_ref();
796 let sub_ref =
797 unsafe { InstanceRef::from_pin_ref(sub_pin.borrow(), guard) };
798 row_runtime_child_count(ci, sub_ref)
799 })
800 .max()
801 .unwrap_or(0)
802 }
803 Some(ci) => ci.len(),
804 None => 1,
805 };
806 repeater_steps.push(item_count as u32);
807 }
808 }
809 repeater_steps
810}
811
812fn grid_layout_constraints(
813 grid_layout: &i_slint_compiler::layout::GridLayout,
814 orientation: Orientation,
815 ctx: &mut EvalLocalContext,
816 repeater_steps: &[u32],
817) -> Vec<core_layout::LayoutItemInfo> {
818 let component = ctx.component_instance;
819 let expr_eval = |nr: &NamedReference| -> f32 {
820 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
821 };
822 let mut constraints = Vec::with_capacity(grid_layout.elems.len());
823
824 let mut repeater_idx = 0usize;
825 for layout_elem in grid_layout.elems.iter() {
826 if layout_elem.item.element.borrow().repeated.is_some() {
827 let component_vec = repeater_instances(component, &layout_elem.item.element);
828 let child_items = layout_elem.cell.borrow().child_items.clone();
829 let has_children = child_items.is_some();
830 if has_children {
831 let ci = child_items.as_ref().unwrap();
833 let step = repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
834 for sub_comp in &component_vec {
835 let per_instance_start = constraints.len();
836 generativity::make_guard!(guard);
838 let sub_pin = sub_comp.as_pin_ref();
839 let sub_borrow = sub_pin.borrow();
840 let sub_instance_ref = unsafe { InstanceRef::from_pin_ref(sub_borrow, guard) };
841 let expr_eval = |nr: &NamedReference| -> f32 {
842 eval::load_property(sub_instance_ref, &nr.element(), nr.name())
843 .unwrap()
844 .try_into()
845 .unwrap()
846 };
847
848 for child_template in ci.iter() {
852 match child_template {
853 i_slint_compiler::layout::RowChildTemplate::Static(child_item) => {
854 let mut layout_info = crate::eval_layout::get_layout_info(
855 &child_item.element,
856 sub_instance_ref,
857 &sub_instance_ref.window_adapter(),
858 orientation,
859 );
860 fill_layout_info_constraints(
861 &mut layout_info,
862 &child_item.constraints,
863 orientation,
864 &expr_eval,
865 );
866 constraints
867 .push(core_layout::LayoutItemInfo { constraint: layout_info });
868 }
869 i_slint_compiler::layout::RowChildTemplate::Repeated {
870 item: child_item,
871 repeated_element,
872 } => {
873 let inner_instances =
875 repeater_instances(sub_instance_ref, repeated_element);
876 for inner_comp in &inner_instances {
877 let inner_pin = inner_comp.as_pin_ref();
878 let mut layout_info =
879 inner_pin.layout_item_info(to_runtime(orientation), None);
880 generativity::make_guard!(inner_guard);
883 let inner_borrow = inner_pin.borrow();
884 let inner_instance_ref = unsafe {
885 InstanceRef::from_pin_ref(inner_borrow, inner_guard)
886 };
887 let inner_expr_eval = |nr: &NamedReference| -> f32 {
888 eval::load_property(
889 inner_instance_ref,
890 &nr.element(),
891 nr.name(),
892 )
893 .unwrap()
894 .try_into()
895 .unwrap()
896 };
897 fill_layout_info_constraints(
898 &mut layout_info.constraint,
899 &child_item.constraints,
900 orientation,
901 &inner_expr_eval,
902 );
903 constraints.push(layout_info);
904 }
905 }
906 }
907 }
908 let pushed = constraints.len() - per_instance_start;
911 for _ in pushed..step {
912 constraints.push(core_layout::LayoutItemInfo::default());
913 }
914 }
915 } else {
916 constraints.extend(
918 component_vec
919 .iter()
920 .map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
921 );
922 }
923 repeater_idx += 1;
924 } else {
925 let mut layout_info = get_layout_info(
926 &layout_elem.item.element,
927 component,
928 &component.window_adapter(),
929 orientation,
930 );
931 fill_layout_info_constraints(
932 &mut layout_info,
933 &layout_elem.item.constraints,
934 orientation,
935 &expr_eval,
936 );
937 constraints.push(core_layout::LayoutItemInfo { constraint: layout_info });
938 }
939 }
940 constraints
941}
942
943fn box_layout_data(
945 box_layout: &i_slint_compiler::layout::BoxLayout,
946 orientation: Orientation,
947 component: InstanceRef,
948 expr_eval: &impl Fn(&NamedReference) -> f32,
949 mut repeater_indices: Option<&mut Vec<u32>>,
950) -> (Vec<core_layout::LayoutItemInfo>, i_slint_core::items::LayoutAlignment) {
951 let window_adapter = component.window_adapter();
952 let mut cells = Vec::with_capacity(box_layout.elems.len());
953 for cell in &box_layout.elems {
954 if cell.element.borrow().repeated.is_some() {
955 let component_vec = repeater_instances(component, &cell.element);
957 if let Some(ri) = repeater_indices.as_mut() {
958 ri.push(cells.len() as _);
959 ri.push(component_vec.len() as _);
960 }
961 cells.extend(
962 component_vec
963 .iter()
964 .map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
965 );
966 } else {
967 let mut layout_info =
969 get_layout_info(&cell.element, component, &window_adapter, orientation);
970 fill_layout_info_constraints(
971 &mut layout_info,
972 &cell.constraints,
973 orientation,
974 &expr_eval,
975 );
976 cells.push(core_layout::LayoutItemInfo { constraint: layout_info });
977 }
978 }
979 let alignment = box_layout
980 .geometry
981 .alignment
982 .as_ref()
983 .map(|nr| {
984 eval::load_property(component, &nr.element(), nr.name())
985 .unwrap()
986 .try_into()
987 .unwrap_or_default()
988 })
989 .unwrap_or_default();
990 (cells, alignment)
991}
992
993pub(crate) fn fill_layout_info_constraints(
994 layout_info: &mut core_layout::LayoutInfo,
995 constraints: &LayoutConstraints,
996 orientation: Orientation,
997 expr_eval: &impl Fn(&NamedReference) -> f32,
998) {
999 let is_percent =
1000 |nr: &NamedReference| Expression::PropertyReference(nr.clone()).ty() == Type::Percent;
1001
1002 match orientation {
1003 Orientation::Horizontal => {
1004 if let Some(e) = constraints.min_width.as_ref() {
1005 if !is_percent(e) {
1006 layout_info.min = expr_eval(e)
1007 } else {
1008 layout_info.min_percent = expr_eval(e)
1009 }
1010 }
1011 if let Some(e) = constraints.max_width.as_ref() {
1012 if !is_percent(e) {
1013 layout_info.max = expr_eval(e)
1014 } else {
1015 layout_info.max_percent = expr_eval(e)
1016 }
1017 }
1018 if let Some(e) = constraints.preferred_width.as_ref() {
1019 layout_info.preferred = expr_eval(e);
1020 }
1021 if let Some(e) = constraints.horizontal_stretch.as_ref() {
1022 layout_info.stretch = expr_eval(e);
1023 }
1024 }
1025 Orientation::Vertical => {
1026 if let Some(e) = constraints.min_height.as_ref() {
1027 if !is_percent(e) {
1028 layout_info.min = expr_eval(e)
1029 } else {
1030 layout_info.min_percent = expr_eval(e)
1031 }
1032 }
1033 if let Some(e) = constraints.max_height.as_ref() {
1034 if !is_percent(e) {
1035 layout_info.max = expr_eval(e)
1036 } else {
1037 layout_info.max_percent = expr_eval(e)
1038 }
1039 }
1040 if let Some(e) = constraints.preferred_height.as_ref() {
1041 layout_info.preferred = expr_eval(e);
1042 }
1043 if let Some(e) = constraints.vertical_stretch.as_ref() {
1044 layout_info.stretch = expr_eval(e);
1045 }
1046 }
1047 }
1048}
1049
1050pub(crate) fn get_layout_info(
1052 elem: &ElementRc,
1053 component: InstanceRef,
1054 window_adapter: &Rc<dyn WindowAdapter>,
1055 orientation: Orientation,
1056) -> core_layout::LayoutInfo {
1057 get_layout_info_with_constraint(elem, component, window_adapter, orientation, -1.)
1058}
1059
1060fn get_layout_info_with_constraint(
1061 elem: &ElementRc,
1062 component: InstanceRef,
1063 window_adapter: &Rc<dyn WindowAdapter>,
1064 orientation: Orientation,
1065 cross_axis_constraint: f32,
1066) -> core_layout::LayoutInfo {
1067 let elem = elem.borrow();
1068 if let Some(nr) = elem.layout_info_prop(orientation) {
1069 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
1070 } else {
1071 let item = &component
1072 .description
1073 .items
1074 .get(elem.id.as_str())
1075 .unwrap_or_else(|| panic!("Internal error: Item {} not found", elem.id));
1076 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
1077
1078 unsafe {
1079 item.item_from_item_tree(component.as_ptr()).as_ref().layout_info(
1080 to_runtime(orientation),
1081 cross_axis_constraint,
1082 window_adapter,
1083 &ItemRc::new(vtable::VRc::into_dyn(item_comp), item.item_index()),
1084 )
1085 }
1086 }
1087}