1use crate::dynamic_item_tree::InstanceRef;
5use crate::eval::{self, EvalLocalContext};
6use crate::Value;
7use i_slint_compiler::expression_tree::Expression;
8use i_slint_compiler::langtype::Type;
9use i_slint_compiler::layout::{Layout, LayoutConstraints, LayoutGeometry, Orientation};
10use i_slint_compiler::namedreference::NamedReference;
11use i_slint_compiler::object_tree::ElementRc;
12use i_slint_core::items::{DialogButtonRole, ItemRc};
13use i_slint_core::layout::{self as core_layout};
14use i_slint_core::model::RepeatedItemTree;
15use i_slint_core::slice::Slice;
16use i_slint_core::window::WindowAdapter;
17use std::rc::Rc;
18use std::str::FromStr;
19
20pub(crate) fn to_runtime(o: Orientation) -> core_layout::Orientation {
21 match o {
22 Orientation::Horizontal => core_layout::Orientation::Horizontal,
23 Orientation::Vertical => core_layout::Orientation::Vertical,
24 }
25}
26
27pub(crate) fn from_runtime(o: core_layout::Orientation) -> Orientation {
28 match o {
29 core_layout::Orientation::Horizontal => Orientation::Horizontal,
30 core_layout::Orientation::Vertical => Orientation::Vertical,
31 }
32}
33
34pub(crate) fn compute_layout_info(
35 lay: &Layout,
36 orientation: Orientation,
37 local_context: &mut EvalLocalContext,
38) -> Value {
39 let component = local_context.component_instance;
40 let expr_eval = |nr: &NamedReference| -> f32 {
41 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
42 };
43 match lay {
44 Layout::GridLayout(grid_layout) => {
45 let cells = grid_layout_data(grid_layout, orientation, component, &expr_eval);
46 let (padding, spacing) =
47 padding_and_spacing(&grid_layout.geometry, orientation, &expr_eval);
48 core_layout::grid_layout_info(Slice::from(cells.as_slice()), spacing, &padding).into()
49 }
50 Layout::BoxLayout(box_layout) => {
51 let (cells, alignment) =
52 box_layout_data(box_layout, orientation, component, &expr_eval, None);
53 let (padding, spacing) =
54 padding_and_spacing(&box_layout.geometry, orientation, &expr_eval);
55 if orientation == box_layout.orientation {
56 core_layout::box_layout_info(
57 Slice::from(cells.as_slice()),
58 spacing,
59 &padding,
60 alignment,
61 )
62 } else {
63 core_layout::box_layout_info_ortho(Slice::from(cells.as_slice()), &padding)
64 }
65 .into()
66 }
67 }
68}
69
70pub(crate) fn solve_layout(
71 lay: &Layout,
72 orientation: Orientation,
73 local_context: &mut EvalLocalContext,
74) -> Value {
75 let component = local_context.component_instance;
76 let expr_eval = |nr: &NamedReference| -> f32 {
77 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
78 };
79
80 match lay {
81 Layout::GridLayout(grid_layout) => {
82 let mut cells = grid_layout_data(grid_layout, orientation, component, &expr_eval);
83 if let (Some(buttons_roles), Orientation::Horizontal) =
84 (&grid_layout.dialog_button_roles, orientation)
85 {
86 let roles = buttons_roles
87 .iter()
88 .map(|r| DialogButtonRole::from_str(r).unwrap())
89 .collect::<Vec<_>>();
90 core_layout::reorder_dialog_button_layout(&mut cells, &roles);
91 }
92
93 let (padding, spacing) =
94 padding_and_spacing(&grid_layout.geometry, orientation, &expr_eval);
95
96 let size_ref = grid_layout.geometry.rect.size_reference(orientation);
97 core_layout::solve_grid_layout(&core_layout::GridLayoutData {
98 size: size_ref.map(expr_eval).unwrap_or(0.),
99 spacing,
100 padding,
101 cells: Slice::from(cells.as_slice()),
102 })
103 .into()
104 }
105 Layout::BoxLayout(box_layout) => {
106 let mut repeated_indices = Vec::new();
107 let (cells, alignment) = box_layout_data(
108 box_layout,
109 orientation,
110 component,
111 &expr_eval,
112 Some(&mut repeated_indices),
113 );
114 let (padding, spacing) =
115 padding_and_spacing(&box_layout.geometry, orientation, &expr_eval);
116 let size_ref = match orientation {
117 Orientation::Horizontal => &box_layout.geometry.rect.width_reference,
118 Orientation::Vertical => &box_layout.geometry.rect.height_reference,
119 };
120 core_layout::solve_box_layout(
121 &core_layout::BoxLayoutData {
122 size: size_ref.as_ref().map(expr_eval).unwrap_or(0.),
123 spacing,
124 padding,
125 alignment,
126 cells: Slice::from(cells.as_slice()),
127 },
128 Slice::from(repeated_indices.as_slice()),
129 )
130 .into()
131 }
132 }
133}
134
135fn padding_and_spacing(
136 layout_geometry: &LayoutGeometry,
137 orientation: Orientation,
138 expr_eval: &impl Fn(&NamedReference) -> f32,
139) -> (core_layout::Padding, f32) {
140 let spacing = layout_geometry.spacing.orientation(orientation).map_or(0., expr_eval);
141 let (begin, end) = layout_geometry.padding.begin_end(orientation);
142 let padding =
143 core_layout::Padding { begin: begin.map_or(0., expr_eval), end: end.map_or(0., expr_eval) };
144 (padding, spacing)
145}
146
147fn grid_layout_data(
149 grid_layout: &i_slint_compiler::layout::GridLayout,
150 orientation: Orientation,
151 component: InstanceRef,
152 expr_eval: &impl Fn(&NamedReference) -> f32,
153) -> Vec<core_layout::GridLayoutCellData> {
154 let cells = grid_layout
155 .elems
156 .iter()
157 .map(|cell| {
158 let mut layout_info = get_layout_info(
159 &cell.item.element,
160 component,
161 &component.window_adapter(),
162 orientation,
163 );
164 fill_layout_info_constraints(
165 &mut layout_info,
166 &cell.item.constraints,
167 orientation,
168 &expr_eval,
169 );
170 let (col_or_row, span) = cell.col_or_row_and_span(orientation);
171 core_layout::GridLayoutCellData { col_or_row, span, constraint: layout_info }
172 })
173 .collect::<Vec<_>>();
174 cells
175}
176
177fn box_layout_data(
178 box_layout: &i_slint_compiler::layout::BoxLayout,
179 orientation: Orientation,
180 component: InstanceRef,
181 expr_eval: &impl Fn(&NamedReference) -> f32,
182 mut repeater_indices: Option<&mut Vec<u32>>,
183) -> (Vec<core_layout::BoxLayoutCellData>, i_slint_core::items::LayoutAlignment) {
184 let window_adapter = component.window_adapter();
185 let mut cells = Vec::with_capacity(box_layout.elems.len());
186 for cell in &box_layout.elems {
187 if cell.element.borrow().repeated.is_some() {
188 generativity::make_guard!(guard);
189 let rep = crate::dynamic_item_tree::get_repeater_by_name(
190 component,
191 cell.element.borrow().id.as_str(),
192 guard,
193 );
194 rep.0.as_ref().ensure_updated(|| {
195 let instance = crate::dynamic_item_tree::instantiate(
196 rep.1.clone(),
197 component.self_weak().get().cloned(),
198 None,
199 None,
200 Default::default(),
201 );
202 instance
203 });
204 let component_vec = rep.0.as_ref().instances_vec();
205 if let Some(ri) = repeater_indices.as_mut() {
206 ri.push(cells.len() as _);
207 ri.push(component_vec.len() as _);
208 }
209 cells.extend(
210 component_vec
211 .iter()
212 .map(|x| x.as_pin_ref().box_layout_data(to_runtime(orientation))),
213 );
214 } else {
215 let mut layout_info =
216 get_layout_info(&cell.element, component, &window_adapter, orientation);
217 fill_layout_info_constraints(
218 &mut layout_info,
219 &cell.constraints,
220 orientation,
221 &expr_eval,
222 );
223 cells.push(core_layout::BoxLayoutCellData { constraint: layout_info });
224 }
225 }
226 let alignment = box_layout
227 .geometry
228 .alignment
229 .as_ref()
230 .map(|nr| {
231 eval::load_property(component, &nr.element(), nr.name())
232 .unwrap()
233 .try_into()
234 .unwrap_or_default()
235 })
236 .unwrap_or_default();
237 (cells, alignment)
238}
239
240pub(crate) fn fill_layout_info_constraints(
241 layout_info: &mut core_layout::LayoutInfo,
242 constraints: &LayoutConstraints,
243 orientation: Orientation,
244 expr_eval: &impl Fn(&NamedReference) -> f32,
245) {
246 let is_percent =
247 |nr: &NamedReference| Expression::PropertyReference(nr.clone()).ty() == Type::Percent;
248
249 match orientation {
250 Orientation::Horizontal => {
251 if let Some(e) = constraints.min_width.as_ref() {
252 if !is_percent(e) {
253 layout_info.min = expr_eval(e)
254 } else {
255 layout_info.min_percent = expr_eval(e)
256 }
257 }
258 if let Some(e) = constraints.max_width.as_ref() {
259 if !is_percent(e) {
260 layout_info.max = expr_eval(e)
261 } else {
262 layout_info.max_percent = expr_eval(e)
263 }
264 }
265 if let Some(e) = constraints.preferred_width.as_ref() {
266 layout_info.preferred = expr_eval(e);
267 }
268 if let Some(e) = constraints.horizontal_stretch.as_ref() {
269 layout_info.stretch = expr_eval(e);
270 }
271 }
272 Orientation::Vertical => {
273 if let Some(e) = constraints.min_height.as_ref() {
274 if !is_percent(e) {
275 layout_info.min = expr_eval(e)
276 } else {
277 layout_info.min_percent = expr_eval(e)
278 }
279 }
280 if let Some(e) = constraints.max_height.as_ref() {
281 if !is_percent(e) {
282 layout_info.max = expr_eval(e)
283 } else {
284 layout_info.max_percent = expr_eval(e)
285 }
286 }
287 if let Some(e) = constraints.preferred_height.as_ref() {
288 layout_info.preferred = expr_eval(e);
289 }
290 if let Some(e) = constraints.vertical_stretch.as_ref() {
291 layout_info.stretch = expr_eval(e);
292 }
293 }
294 }
295}
296
297pub(crate) fn get_layout_info(
299 elem: &ElementRc,
300 component: InstanceRef,
301 window_adapter: &Rc<dyn WindowAdapter>,
302 orientation: Orientation,
303) -> core_layout::LayoutInfo {
304 let elem = elem.borrow();
305 if let Some(nr) = elem.layout_info_prop(orientation) {
306 eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap()
307 } else {
308 let item = &component
309 .description
310 .items
311 .get(elem.id.as_str())
312 .unwrap_or_else(|| panic!("Internal error: Item {} not found", elem.id));
313 let item_comp = component.self_weak().get().unwrap().upgrade().unwrap();
314
315 unsafe {
316 item.item_from_item_tree(component.as_ptr()).as_ref().layout_info(
317 to_runtime(orientation),
318 window_adapter,
319 &ItemRc::new(vtable::VRc::into_dyn(item_comp), item.item_index()),
320 )
321 }
322 }
323}