use crate::Value;
use crate::dynamic_item_tree::InstanceRef;
use crate::eval::{self, EvalLocalContext};
use i_slint_compiler::expression_tree::Expression;
use i_slint_compiler::langtype::Type;
use i_slint_compiler::layout::{
BoxLayout, GridLayout, LayoutConstraints, LayoutGeometry, Orientation, RowColExpr,
};
use i_slint_compiler::namedreference::NamedReference;
use i_slint_compiler::object_tree::ElementRc;
use i_slint_core::items::{DialogButtonRole, FlexboxLayoutDirection, ItemRc};
use i_slint_core::layout::{self as core_layout, GridLayoutInputData, GridLayoutOrganizedData};
use i_slint_core::model::RepeatedItemTree;
use i_slint_core::slice::Slice;
use i_slint_core::window::WindowAdapter;
use std::rc::Rc;
use std::str::FromStr;
pub(crate) fn to_runtime(o: Orientation) -> core_layout::Orientation {
match o {
Orientation::Horizontal => core_layout::Orientation::Horizontal,
Orientation::Vertical => core_layout::Orientation::Vertical,
}
}
pub(crate) fn from_runtime(o: core_layout::Orientation) -> Orientation {
match o {
core_layout::Orientation::Horizontal => Orientation::Horizontal,
core_layout::Orientation::Vertical => Orientation::Vertical,
}
}
pub(crate) fn compute_grid_layout_info(
grid_layout: &GridLayout,
organized_data: &GridLayoutOrganizedData,
orientation: Orientation,
local_context: &mut EvalLocalContext,
) -> Value {
let component = local_context.component_instance;
let expr_eval = |nr: &NamedReference| -> f32 {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
};
let (padding, spacing) = padding_and_spacing(&grid_layout.geometry, orientation, &expr_eval);
let repeater_steps = grid_repeater_steps(grid_layout, local_context);
let repeater_indices = grid_repeater_indices(grid_layout, local_context, &repeater_steps);
let constraints =
grid_layout_constraints(grid_layout, orientation, local_context, &repeater_steps);
core_layout::grid_layout_info(
organized_data.clone(),
Slice::from_slice(constraints.as_slice()),
Slice::from_slice(repeater_indices.as_slice()),
Slice::from_slice(repeater_steps.as_slice()),
spacing,
&padding,
to_runtime(orientation),
)
.into()
}
pub(crate) fn compute_box_layout_info(
box_layout: &BoxLayout,
orientation: Orientation,
local_context: &mut EvalLocalContext,
) -> Value {
let component = local_context.component_instance;
let expr_eval = |nr: &NamedReference| -> f32 {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
};
let (cells, alignment) = box_layout_data(box_layout, orientation, component, &expr_eval, None);
let (padding, spacing) = padding_and_spacing(&box_layout.geometry, orientation, &expr_eval);
if orientation == box_layout.orientation {
core_layout::box_layout_info(Slice::from(cells.as_slice()), spacing, &padding, alignment)
} else {
core_layout::box_layout_info_ortho(Slice::from(cells.as_slice()), &padding)
}
.into()
}
pub(crate) fn organize_grid_layout(
layout: &GridLayout,
local_context: &mut EvalLocalContext,
) -> Value {
let repeater_steps = grid_repeater_steps(layout, local_context);
let cells = grid_layout_input_data(layout, local_context, &repeater_steps);
let repeater_indices = grid_repeater_indices(layout, local_context, &repeater_steps);
if let Some(buttons_roles) = &layout.dialog_button_roles {
let roles = buttons_roles
.iter()
.map(|r| DialogButtonRole::from_str(r).unwrap())
.collect::<Vec<_>>();
core_layout::organize_dialog_button_layout(
Slice::from_slice(cells.as_slice()),
Slice::from_slice(roles.as_slice()),
)
.into()
} else {
core_layout::organize_grid_layout(
Slice::from_slice(cells.as_slice()),
Slice::from_slice(repeater_indices.as_slice()),
Slice::from_slice(repeater_steps.as_slice()),
)
.into()
}
}
pub(crate) fn solve_grid_layout(
organized_data: &GridLayoutOrganizedData,
grid_layout: &GridLayout,
orientation: Orientation,
local_context: &mut EvalLocalContext,
) -> Value {
let component = local_context.component_instance;
let expr_eval = |nr: &NamedReference| -> f32 {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
};
let repeater_steps = grid_repeater_steps(grid_layout, local_context);
let repeater_indices = grid_repeater_indices(grid_layout, local_context, &repeater_steps);
let constraints =
grid_layout_constraints(grid_layout, orientation, local_context, &repeater_steps);
let (padding, spacing) = padding_and_spacing(&grid_layout.geometry, orientation, &expr_eval);
let size_ref = grid_layout.geometry.rect.size_reference(orientation);
let data = core_layout::GridLayoutData {
size: size_ref.map(expr_eval).unwrap_or(0.),
spacing,
padding,
organized_data: organized_data.clone(),
};
core_layout::solve_grid_layout(
&data,
Slice::from_slice(constraints.as_slice()),
to_runtime(orientation),
Slice::from_slice(repeater_indices.as_slice()),
Slice::from_slice(repeater_steps.as_slice()),
)
.into()
}
pub(crate) fn solve_box_layout(
box_layout: &BoxLayout,
orientation: Orientation,
local_context: &mut EvalLocalContext,
) -> Value {
let component = local_context.component_instance;
let expr_eval = |nr: &NamedReference| -> f32 {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
};
let mut repeated_indices = Vec::new();
let (cells, alignment) = box_layout_data(
box_layout,
orientation,
component,
&expr_eval,
Some(&mut repeated_indices),
);
let (padding, spacing) = padding_and_spacing(&box_layout.geometry, orientation, &expr_eval);
let size_ref = match orientation {
Orientation::Horizontal => &box_layout.geometry.rect.width_reference,
Orientation::Vertical => &box_layout.geometry.rect.height_reference,
};
core_layout::solve_box_layout(
&core_layout::BoxLayoutData {
size: size_ref.as_ref().map(expr_eval).unwrap_or(0.),
spacing,
padding,
alignment,
cells: Slice::from(cells.as_slice()),
},
Slice::from(repeated_indices.as_slice()),
)
.into()
}
pub(crate) fn solve_flexbox_layout(
flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
local_context: &mut EvalLocalContext,
) -> Value {
let component = local_context.component_instance;
let expr_eval = |nr: &NamedReference| -> f32 {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
};
let width_ref = &flexbox_layout.geometry.rect.width_reference;
let height_ref = &flexbox_layout.geometry.rect.height_reference;
let direction = flexbox_layout_direction(flexbox_layout, local_context);
let container_width_for_cells = match direction {
i_slint_core::items::FlexboxLayoutDirection::Column
| i_slint_core::items::FlexboxLayoutDirection::ColumnReverse => {
width_ref.as_ref().map(&expr_eval)
}
_ => None,
};
let (cells_h, cells_v, repeated_indices) = flexbox_layout_data(
flexbox_layout,
component,
&expr_eval,
local_context,
container_width_for_cells,
);
let alignment = flexbox_layout
.geometry
.alignment
.as_ref()
.map_or(i_slint_core::items::LayoutAlignment::default(), |nr| {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
});
let align_content = flexbox_layout
.align_content
.as_ref()
.map_or(i_slint_core::items::FlexboxLayoutAlignContent::default(), |nr| {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
});
let align_items = flexbox_layout
.align_items
.as_ref()
.map_or(i_slint_core::items::FlexboxLayoutAlignItems::default(), |nr| {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
});
let flex_wrap = flexbox_layout
.flex_wrap
.as_ref()
.map_or(i_slint_core::items::FlexboxLayoutWrap::default(), |nr| {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
});
let (padding_h, spacing_h) =
padding_and_spacing(&flexbox_layout.geometry, Orientation::Horizontal, &expr_eval);
let (padding_v, spacing_v) =
padding_and_spacing(&flexbox_layout.geometry, Orientation::Vertical, &expr_eval);
let data = core_layout::FlexboxLayoutData {
width: width_ref.as_ref().map(&expr_eval).unwrap_or(0.),
height: height_ref.as_ref().map(&expr_eval).unwrap_or(0.),
spacing_h,
spacing_v,
padding_h,
padding_v,
alignment,
direction,
align_content,
align_items,
flex_wrap,
cells_h: Slice::from(cells_h.as_slice()),
cells_v: Slice::from(cells_v.as_slice()),
};
let ri = Slice::from(repeated_indices.as_slice());
let window_adapter = component.window_adapter();
let mut child_elem_ids: Vec<Option<smol_str::SmolStr>> = Vec::new();
for layout_elem in &flexbox_layout.elems {
if layout_elem.item.element.borrow().repeated.is_some() {
let component_vec = repeater_instances(component, &layout_elem.item.element);
for _ in 0..component_vec.len() {
child_elem_ids.push(None);
}
} else {
child_elem_ids.push(Some(layout_elem.item.element.borrow().id.clone()));
}
}
let mut measure = |child_index: usize,
known_w: Option<f32>,
known_h: Option<f32>|
-> (f32, f32) {
let default_w = cells_h.get(child_index).map_or(0., |c| c.constraint.preferred_bounded());
let default_h = cells_v.get(child_index).map_or(0., |c| c.constraint.preferred_bounded());
let w = known_w.unwrap_or(default_w);
let h = known_h.unwrap_or(default_h);
let elem_id = match child_elem_ids.get(child_index) {
Some(Some(id)) => id,
_ => return (w, h),
};
let item_within = match component.description.items.get(elem_id.as_str()) {
Some(i) => i,
None => return (w, h),
};
if known_w.is_some() && known_h.is_none() {
let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
let item_rc = ItemRc::new(vtable::VRc::into_dyn(item_comp), item_within.item_index());
let item = unsafe { item_within.item_from_item_tree(component.as_ptr()) };
let v_info = item.as_ref().layout_info(
to_runtime(Orientation::Vertical),
w,
&window_adapter,
&item_rc,
);
return (w, v_info.preferred_bounded());
}
if known_h.is_some() && known_w.is_none() {
let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
let item_rc = ItemRc::new(vtable::VRc::into_dyn(item_comp), item_within.item_index());
let item = unsafe { item_within.item_from_item_tree(component.as_ptr()) };
let h_info = item.as_ref().layout_info(
to_runtime(Orientation::Horizontal),
h,
&window_adapter,
&item_rc,
);
return (h_info.preferred_bounded(), h);
}
(w, h)
};
core_layout::solve_flexbox_layout_with_measure(&data, ri, Some(&mut measure)).into()
}
fn flexbox_layout_direction(
flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
local_context: &EvalLocalContext,
) -> FlexboxLayoutDirection {
flexbox_layout
.direction
.as_ref()
.and_then(|nr| {
let value =
eval::load_property(local_context.component_instance, &nr.element(), nr.name())
.ok()?;
if let Value::EnumerationValue(_, variant) = &value {
match variant.as_str() {
"row" => Some(FlexboxLayoutDirection::Row),
"row-reverse" => Some(FlexboxLayoutDirection::RowReverse),
"column" => Some(FlexboxLayoutDirection::Column),
"column-reverse" => Some(FlexboxLayoutDirection::ColumnReverse),
_ => None,
}
} else {
None
}
})
.unwrap_or(FlexboxLayoutDirection::Row)
}
pub(crate) fn compute_flexbox_layout_info(
flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
orientation: Orientation,
local_context: &mut EvalLocalContext,
) -> Value {
let component = local_context.component_instance;
let expr_eval = |nr: &NamedReference| -> f32 {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
};
let (cells_h, cells_v, _repeated_indices) =
flexbox_layout_data(flexbox_layout, component, &expr_eval, local_context, None);
let direction = flexbox_layout_direction(flexbox_layout, local_context);
let is_main_axis = matches!(
(direction, orientation),
(FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse, Orientation::Horizontal)
| (
FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse,
Orientation::Vertical
)
);
let (padding_h, spacing_h) =
padding_and_spacing(&flexbox_layout.geometry, Orientation::Horizontal, &expr_eval);
let (padding_v, spacing_v) =
padding_and_spacing(&flexbox_layout.geometry, Orientation::Vertical, &expr_eval);
let flex_wrap = flexbox_layout
.flex_wrap
.as_ref()
.map_or(i_slint_core::items::FlexboxLayoutWrap::default(), |nr| {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
});
if is_main_axis {
let (cells, spacing, padding) = match orientation {
Orientation::Horizontal => (&cells_h, spacing_h, &padding_h),
Orientation::Vertical => (&cells_v, spacing_v, &padding_v),
};
core_layout::flexbox_layout_info_main_axis(
Slice::from(cells.as_slice()),
spacing,
padding,
flex_wrap,
)
.into()
} else {
let constraint_size = match orientation {
Orientation::Horizontal => {
let height_ref = &flexbox_layout.geometry.rect.height_reference;
height_ref.as_ref().map(&expr_eval).unwrap_or(0.)
}
Orientation::Vertical => {
let width_ref = &flexbox_layout.geometry.rect.width_reference;
width_ref.as_ref().map(&expr_eval).unwrap_or(0.)
}
};
core_layout::flexbox_layout_info_cross_axis(
Slice::from(cells_h.as_slice()),
Slice::from(cells_v.as_slice()),
spacing_h,
spacing_v,
&padding_h,
&padding_v,
direction,
flex_wrap,
constraint_size,
)
.into()
}
}
fn flexbox_layout_data(
flexbox_layout: &i_slint_compiler::layout::FlexboxLayout,
component: InstanceRef,
expr_eval: &impl Fn(&NamedReference) -> f32,
_local_context: &mut EvalLocalContext,
container_width: Option<f32>,
) -> (Vec<core_layout::FlexboxLayoutItemInfo>, Vec<core_layout::FlexboxLayoutItemInfo>, Vec<u32>) {
let window_adapter = component.window_adapter();
let mut cells_h = Vec::with_capacity(flexbox_layout.elems.len());
let mut cells_v = Vec::with_capacity(flexbox_layout.elems.len());
let mut repeated_indices = Vec::new();
struct ChildInfo {
flex_grow: f32,
flex_shrink: f32,
flex_basis: f32,
flex_align_self: i_slint_core::items::FlexboxLayoutAlignSelf,
flex_order: i32,
}
let mut static_children: Vec<Option<ChildInfo>> = Vec::new();
for layout_elem in &flexbox_layout.elems {
if layout_elem.item.element.borrow().repeated.is_some() {
let component_vec = repeater_instances(component, &layout_elem.item.element);
repeated_indices.push(cells_h.len() as u32);
repeated_indices.push(component_vec.len() as u32);
cells_h.extend(component_vec.iter().map(|x| {
x.as_pin_ref().flexbox_layout_item_info(to_runtime(Orientation::Horizontal), None)
}));
cells_v.extend(component_vec.iter().map(|x| {
x.as_pin_ref().flexbox_layout_item_info(to_runtime(Orientation::Vertical), None)
}));
for _ in 0..component_vec.len() {
static_children.push(None);
}
} else {
let mut layout_info_h = get_layout_info(
&layout_elem.item.element,
component,
&window_adapter,
Orientation::Horizontal,
);
fill_layout_info_constraints(
&mut layout_info_h,
&layout_elem.item.constraints,
Orientation::Horizontal,
expr_eval,
);
let flex_grow = layout_elem.flex_grow.as_ref().map(&expr_eval).unwrap_or(0.0);
let flex_shrink = layout_elem.flex_shrink.as_ref().map(&expr_eval).unwrap_or(1.0);
let flex_basis = layout_elem.flex_basis.as_ref().map(&expr_eval).unwrap_or(-1.0);
let align_self = layout_elem
.align_self
.as_ref()
.map(|nr| {
eval::load_property(component, &nr.element(), nr.name())
.unwrap()
.try_into()
.unwrap()
})
.unwrap_or(i_slint_core::items::FlexboxLayoutAlignSelf::default());
let order = layout_elem.order.as_ref().map(expr_eval).unwrap_or(0.0) as i32;
cells_h.push(core_layout::FlexboxLayoutItemInfo {
constraint: layout_info_h,
flex_grow,
flex_shrink,
flex_basis,
flex_align_self: align_self,
flex_order: order,
});
cells_v.push(core_layout::FlexboxLayoutItemInfo::default());
static_children.push(Some(ChildInfo {
flex_grow,
flex_shrink,
flex_basis,
flex_align_self: align_self,
flex_order: order,
}));
}
}
let mut cell_idx = 0usize;
for layout_elem in &flexbox_layout.elems {
if layout_elem.item.element.borrow().repeated.is_some() {
let component_vec = repeater_instances(component, &layout_elem.item.element);
cell_idx += component_vec.len();
} else {
let width_constraint =
container_width.unwrap_or_else(|| cells_h[cell_idx].constraint.preferred_bounded());
let mut layout_info_v = get_layout_info_with_constraint(
&layout_elem.item.element,
component,
&window_adapter,
Orientation::Vertical,
width_constraint,
);
fill_layout_info_constraints(
&mut layout_info_v,
&layout_elem.item.constraints,
Orientation::Vertical,
expr_eval,
);
if let Some(info) = &static_children[cell_idx] {
cells_v[cell_idx] = core_layout::FlexboxLayoutItemInfo {
constraint: layout_info_v,
flex_grow: info.flex_grow,
flex_shrink: info.flex_shrink,
flex_basis: info.flex_basis,
flex_align_self: info.flex_align_self,
flex_order: info.flex_order,
};
}
cell_idx += 1;
}
}
(cells_h, cells_v, repeated_indices)
}
fn padding_and_spacing(
layout_geometry: &LayoutGeometry,
orientation: Orientation,
expr_eval: &impl Fn(&NamedReference) -> f32,
) -> (core_layout::Padding, f32) {
let spacing = layout_geometry.spacing.orientation(orientation).map_or(0., expr_eval);
let (begin, end) = layout_geometry.padding.begin_end(orientation);
let padding =
core_layout::Padding { begin: begin.map_or(0., expr_eval), end: end.map_or(0., expr_eval) };
(padding, spacing)
}
fn repeater_instances(
component: InstanceRef,
elem: &ElementRc,
) -> Vec<crate::dynamic_item_tree::DynamicComponentVRc> {
generativity::make_guard!(guard);
let rep =
crate::dynamic_item_tree::get_repeater_by_name(component, elem.borrow().id.as_str(), guard);
let extra_data = component.description.extra_data_offset.apply(component.as_ref());
rep.0.as_ref().ensure_updated(|| {
crate::dynamic_item_tree::instantiate(
rep.1.clone(),
component.self_weak().get().cloned(),
None,
None,
extra_data.globals.get().unwrap().clone(),
)
});
rep.0.as_ref().instances_vec()
}
fn grid_layout_input_data(
grid_layout: &i_slint_compiler::layout::GridLayout,
ctx: &EvalLocalContext,
repeater_steps: &[u32],
) -> Vec<GridLayoutInputData> {
let component = ctx.component_instance;
let mut result = Vec::with_capacity(grid_layout.elems.len());
let mut after_repeater_in_same_row = false;
let mut new_row = true;
let mut repeater_idx = 0usize;
for elem in grid_layout.elems.iter() {
let eval_or_default = |expr: &RowColExpr, component: InstanceRef| match expr {
RowColExpr::Literal(value) => *value as f32,
RowColExpr::Auto => i_slint_common::ROW_COL_AUTO,
RowColExpr::Named(nr) => {
eval::load_property(component, &nr.element(), nr.name())
.unwrap()
.try_into()
.unwrap()
}
};
let cell_new_row = elem.cell.borrow().new_row;
if cell_new_row {
after_repeater_in_same_row = false;
}
if elem.item.element.borrow().repeated.is_some() {
let component_vec = repeater_instances(component, &elem.item.element);
new_row = cell_new_row;
for erased_sub_comp in &component_vec {
generativity::make_guard!(guard);
let sub_comp = erased_sub_comp.as_pin_ref();
let sub_instance_ref =
unsafe { InstanceRef::from_pin_ref(sub_comp.borrow(), guard) };
if let Some(children) = elem.cell.borrow().child_items.as_ref() {
new_row = true;
let start_count = result.len();
for child_template in children {
match child_template {
i_slint_compiler::layout::RowChildTemplate::Static(child_item) => {
let (row_val, col_val, rowspan_val, colspan_val) = {
let element_ref = child_item.element.borrow();
let child_cell =
element_ref.grid_layout_cell.as_ref().unwrap().borrow();
(
eval_or_default(&child_cell.row_expr, sub_instance_ref),
eval_or_default(&child_cell.col_expr, sub_instance_ref),
eval_or_default(&child_cell.rowspan_expr, sub_instance_ref),
eval_or_default(&child_cell.colspan_expr, sub_instance_ref),
)
};
result.push(GridLayoutInputData {
new_row,
col: col_val,
row: row_val,
colspan: colspan_val,
rowspan: rowspan_val,
});
new_row = false;
}
i_slint_compiler::layout::RowChildTemplate::Repeated {
repeated_element,
..
} => {
let inner_instances =
repeater_instances(sub_instance_ref, repeated_element);
for i in 0..inner_instances.len() {
result.push(GridLayoutInputData {
new_row: i == 0 && new_row,
..Default::default()
});
}
if !inner_instances.is_empty() {
new_row = false;
}
}
}
}
let cells_pushed = result.len() - start_count;
let expected_step =
repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
for _ in cells_pushed..expected_step {
result.push(GridLayoutInputData::default());
}
} else {
let cell = elem.cell.borrow();
let row = eval_or_default(&cell.row_expr, sub_instance_ref);
let col = eval_or_default(&cell.col_expr, sub_instance_ref);
let rowspan = eval_or_default(&cell.rowspan_expr, sub_instance_ref);
let colspan = eval_or_default(&cell.colspan_expr, sub_instance_ref);
result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
new_row = false;
}
}
repeater_idx += 1;
after_repeater_in_same_row = true;
} else {
let new_row =
if cell_new_row || !after_repeater_in_same_row { cell_new_row } else { new_row };
let row = eval_or_default(&elem.cell.borrow().row_expr, component);
let col = eval_or_default(&elem.cell.borrow().col_expr, component);
let rowspan = eval_or_default(&elem.cell.borrow().rowspan_expr, component);
let colspan = eval_or_default(&elem.cell.borrow().colspan_expr, component);
result.push(GridLayoutInputData { new_row, col, row, colspan, rowspan });
}
}
result
}
fn row_runtime_child_count(
child_items: &[i_slint_compiler::layout::RowChildTemplate],
sub_instance_ref: InstanceRef,
) -> usize {
let mut count = 0;
for child in child_items {
if let Some(repeated_element) = child.repeated_element() {
count += repeater_instances(sub_instance_ref, repeated_element).len();
} else {
count += 1;
}
}
count
}
fn grid_repeater_indices(
grid_layout: &i_slint_compiler::layout::GridLayout,
ctx: &mut EvalLocalContext,
repeater_steps: &[u32],
) -> Vec<u32> {
let component = ctx.component_instance;
let mut repeater_indices = Vec::new();
let mut num_cells = 0;
let mut step_idx = 0;
for elem in grid_layout.elems.iter() {
if elem.item.element.borrow().repeated.is_some() {
let component_vec = repeater_instances(component, &elem.item.element);
repeater_indices.push(num_cells as _);
repeater_indices.push(component_vec.len() as _);
let item_count = repeater_steps[step_idx] as usize;
num_cells += component_vec.len() * item_count;
step_idx += 1;
} else {
num_cells += 1;
}
}
repeater_indices
}
fn grid_repeater_steps(
grid_layout: &i_slint_compiler::layout::GridLayout,
ctx: &mut EvalLocalContext,
) -> Vec<u32> {
let component = ctx.component_instance;
let mut repeater_steps = Vec::new();
for elem in grid_layout.elems.iter() {
if elem.item.element.borrow().repeated.is_some() {
let item_count = match &elem.cell.borrow().child_items {
Some(ci)
if ci.iter().any(i_slint_compiler::layout::RowChildTemplate::is_repeated) =>
{
let component_vec = repeater_instances(component, &elem.item.element);
component_vec
.iter()
.map(|sub| {
generativity::make_guard!(guard);
let sub_pin = sub.as_pin_ref();
let sub_ref =
unsafe { InstanceRef::from_pin_ref(sub_pin.borrow(), guard) };
row_runtime_child_count(ci, sub_ref)
})
.max()
.unwrap_or(0)
}
Some(ci) => ci.len(),
None => 1,
};
repeater_steps.push(item_count as u32);
}
}
repeater_steps
}
fn grid_layout_constraints(
grid_layout: &i_slint_compiler::layout::GridLayout,
orientation: Orientation,
ctx: &mut EvalLocalContext,
repeater_steps: &[u32],
) -> Vec<core_layout::LayoutItemInfo> {
let component = ctx.component_instance;
let expr_eval = |nr: &NamedReference| -> f32 {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
};
let mut constraints = Vec::with_capacity(grid_layout.elems.len());
let mut repeater_idx = 0usize;
for layout_elem in grid_layout.elems.iter() {
if layout_elem.item.element.borrow().repeated.is_some() {
let component_vec = repeater_instances(component, &layout_elem.item.element);
let child_items = layout_elem.cell.borrow().child_items.clone();
let has_children = child_items.is_some();
if has_children {
let ci = child_items.as_ref().unwrap();
let step = repeater_steps.get(repeater_idx).copied().unwrap_or(0) as usize;
for sub_comp in &component_vec {
let per_instance_start = constraints.len();
generativity::make_guard!(guard);
let sub_pin = sub_comp.as_pin_ref();
let sub_borrow = sub_pin.borrow();
let sub_instance_ref = unsafe { InstanceRef::from_pin_ref(sub_borrow, guard) };
let expr_eval = |nr: &NamedReference| -> f32 {
eval::load_property(sub_instance_ref, &nr.element(), nr.name())
.unwrap()
.try_into()
.unwrap()
};
for child_template in ci.iter() {
match child_template {
i_slint_compiler::layout::RowChildTemplate::Static(child_item) => {
let mut layout_info = crate::eval_layout::get_layout_info(
&child_item.element,
sub_instance_ref,
&sub_instance_ref.window_adapter(),
orientation,
);
fill_layout_info_constraints(
&mut layout_info,
&child_item.constraints,
orientation,
&expr_eval,
);
constraints
.push(core_layout::LayoutItemInfo { constraint: layout_info });
}
i_slint_compiler::layout::RowChildTemplate::Repeated {
item: child_item,
repeated_element,
} => {
let inner_instances =
repeater_instances(sub_instance_ref, repeated_element);
for inner_comp in &inner_instances {
let inner_pin = inner_comp.as_pin_ref();
let mut layout_info =
inner_pin.layout_item_info(to_runtime(orientation), None);
generativity::make_guard!(inner_guard);
let inner_borrow = inner_pin.borrow();
let inner_instance_ref = unsafe {
InstanceRef::from_pin_ref(inner_borrow, inner_guard)
};
let inner_expr_eval = |nr: &NamedReference| -> f32 {
eval::load_property(
inner_instance_ref,
&nr.element(),
nr.name(),
)
.unwrap()
.try_into()
.unwrap()
};
fill_layout_info_constraints(
&mut layout_info.constraint,
&child_item.constraints,
orientation,
&inner_expr_eval,
);
constraints.push(layout_info);
}
}
}
}
let pushed = constraints.len() - per_instance_start;
for _ in pushed..step {
constraints.push(core_layout::LayoutItemInfo::default());
}
}
} else {
constraints.extend(
component_vec
.iter()
.map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
);
}
repeater_idx += 1;
} else {
let mut layout_info = get_layout_info(
&layout_elem.item.element,
component,
&component.window_adapter(),
orientation,
);
fill_layout_info_constraints(
&mut layout_info,
&layout_elem.item.constraints,
orientation,
&expr_eval,
);
constraints.push(core_layout::LayoutItemInfo { constraint: layout_info });
}
}
constraints
}
fn box_layout_data(
box_layout: &i_slint_compiler::layout::BoxLayout,
orientation: Orientation,
component: InstanceRef,
expr_eval: &impl Fn(&NamedReference) -> f32,
mut repeater_indices: Option<&mut Vec<u32>>,
) -> (Vec<core_layout::LayoutItemInfo>, i_slint_core::items::LayoutAlignment) {
let window_adapter = component.window_adapter();
let mut cells = Vec::with_capacity(box_layout.elems.len());
for cell in &box_layout.elems {
if cell.element.borrow().repeated.is_some() {
let component_vec = repeater_instances(component, &cell.element);
if let Some(ri) = repeater_indices.as_mut() {
ri.push(cells.len() as _);
ri.push(component_vec.len() as _);
}
cells.extend(
component_vec
.iter()
.map(|x| x.as_pin_ref().layout_item_info(to_runtime(orientation), None)),
);
} else {
let mut layout_info =
get_layout_info(&cell.element, component, &window_adapter, orientation);
fill_layout_info_constraints(
&mut layout_info,
&cell.constraints,
orientation,
&expr_eval,
);
cells.push(core_layout::LayoutItemInfo { constraint: layout_info });
}
}
let alignment = box_layout
.geometry
.alignment
.as_ref()
.map(|nr| {
eval::load_property(component, &nr.element(), nr.name())
.unwrap()
.try_into()
.unwrap_or_default()
})
.unwrap_or_default();
(cells, alignment)
}
pub(crate) fn fill_layout_info_constraints(
layout_info: &mut core_layout::LayoutInfo,
constraints: &LayoutConstraints,
orientation: Orientation,
expr_eval: &impl Fn(&NamedReference) -> f32,
) {
let is_percent =
|nr: &NamedReference| Expression::PropertyReference(nr.clone()).ty() == Type::Percent;
match orientation {
Orientation::Horizontal => {
if let Some(e) = constraints.min_width.as_ref() {
if !is_percent(e) {
layout_info.min = expr_eval(e)
} else {
layout_info.min_percent = expr_eval(e)
}
}
if let Some(e) = constraints.max_width.as_ref() {
if !is_percent(e) {
layout_info.max = expr_eval(e)
} else {
layout_info.max_percent = expr_eval(e)
}
}
if let Some(e) = constraints.preferred_width.as_ref() {
layout_info.preferred = expr_eval(e);
}
if let Some(e) = constraints.horizontal_stretch.as_ref() {
layout_info.stretch = expr_eval(e);
}
}
Orientation::Vertical => {
if let Some(e) = constraints.min_height.as_ref() {
if !is_percent(e) {
layout_info.min = expr_eval(e)
} else {
layout_info.min_percent = expr_eval(e)
}
}
if let Some(e) = constraints.max_height.as_ref() {
if !is_percent(e) {
layout_info.max = expr_eval(e)
} else {
layout_info.max_percent = expr_eval(e)
}
}
if let Some(e) = constraints.preferred_height.as_ref() {
layout_info.preferred = expr_eval(e);
}
if let Some(e) = constraints.vertical_stretch.as_ref() {
layout_info.stretch = expr_eval(e);
}
}
}
}
pub(crate) fn get_layout_info(
elem: &ElementRc,
component: InstanceRef,
window_adapter: &Rc<dyn WindowAdapter>,
orientation: Orientation,
) -> core_layout::LayoutInfo {
get_layout_info_with_constraint(elem, component, window_adapter, orientation, -1.)
}
fn get_layout_info_with_constraint(
elem: &ElementRc,
component: InstanceRef,
window_adapter: &Rc<dyn WindowAdapter>,
orientation: Orientation,
cross_axis_constraint: f32,
) -> core_layout::LayoutInfo {
let elem = elem.borrow();
if let Some(nr) = elem.layout_info_prop(orientation) {
eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
} else {
let item = &component
.description
.items
.get(elem.id.as_str())
.unwrap_or_else(|| panic!("Internal error: Item {} not found", elem.id));
let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
unsafe {
item.item_from_item_tree(component.as_ptr()).as_ref().layout_info(
to_runtime(orientation),
cross_axis_constraint,
window_adapter,
&ItemRc::new(vtable::VRc::into_dyn(item_comp), item.item_index()),
)
}
}
}