1use lyon_path::geom::euclid::approxeq::ApproxEq;
7
8use crate::diagnostics::{BuildDiagnostics, DiagnosticLevel, Spanned};
9use crate::expression_tree::*;
10use crate::langtype::ElementType;
11use crate::langtype::Type;
12use crate::layout::*;
13use crate::object_tree::*;
14use crate::typeloader::TypeLoader;
15use crate::typeregister::{TypeRegister, layout_info_type};
16use smol_str::{SmolStr, format_smolstr};
17use std::cell::RefCell;
18use std::collections::HashSet;
19use std::rc::Rc;
20
21pub fn lower_layouts(
22 component: &Rc<Component>,
23 type_loader: &mut TypeLoader,
24 style_metrics: &Rc<Component>,
25 diag: &mut BuildDiagnostics,
26) {
27 recurse_elem_including_sub_components(component, &(), &mut |elem, _| {
29 if check_preferred_size_100(elem, "preferred-width", diag) {
30 elem.borrow_mut().default_fill_parent.0 = true;
31 }
32 if check_preferred_size_100(elem, "preferred-height", diag) {
33 elem.borrow_mut().default_fill_parent.1 = true;
34 }
35 let base = elem.borrow().sub_component().cloned();
36 if let Some(base) = base {
37 let base = base.root_element.borrow();
38 let mut elem_mut = elem.borrow_mut();
39 elem_mut.default_fill_parent.0 |= base.default_fill_parent.0;
40 elem_mut.default_fill_parent.1 |= base.default_fill_parent.1;
41 }
42 });
43
44 *component.root_constraints.borrow_mut() =
45 LayoutConstraints::new(&component.root_element, diag, DiagnosticLevel::Error);
46
47 recurse_elem_including_sub_components(
48 component,
49 &Option::default(),
50 &mut |elem, parent_layout_type| {
51 let component = elem.borrow().enclosing_component.upgrade().unwrap();
52
53 lower_element_layout(
54 &component,
55 elem,
56 &type_loader.global_type_registry.borrow(),
57 style_metrics,
58 parent_layout_type,
59 diag,
60 )
61 },
62 );
63}
64
65fn check_preferred_size_100(elem: &ElementRc, prop: &str, diag: &mut BuildDiagnostics) -> bool {
66 let ret = if let Some(p) = elem.borrow().bindings.get(prop) {
67 if p.borrow().expression.ty() == Type::Percent {
68 if !matches!(p.borrow().expression.ignore_debug_hooks(), Expression::NumberLiteral(val, _) if *val == 100.)
69 {
70 diag.push_error(
71 format!("{prop} must either be a length, or the literal '100%'"),
72 &*p.borrow(),
73 );
74 }
75 true
76 } else {
77 false
78 }
79 } else {
80 false
81 };
82 if ret {
83 elem.borrow_mut().bindings.remove(prop).unwrap();
84 return true;
85 }
86 false
87}
88
89fn lower_element_layout(
92 component: &Rc<Component>,
93 elem: &ElementRc,
94 type_register: &TypeRegister,
95 style_metrics: &Rc<Component>,
96 parent_layout_type: &Option<SmolStr>,
97 diag: &mut BuildDiagnostics,
98) -> Option<SmolStr> {
99 let layout_type = if let ElementType::Builtin(base_type) = &elem.borrow().base_type {
100 Some(base_type.name.clone())
101 } else {
102 None
103 };
104
105 check_no_layout_properties(elem, &layout_type, parent_layout_type, diag);
106
107 match layout_type.as_ref()?.as_str() {
108 "Row" => return layout_type,
109 "GridLayout" => lower_grid_layout(component, elem, diag, type_register),
110 "HorizontalLayout" => lower_box_layout(elem, diag, Orientation::Horizontal),
111 "VerticalLayout" => lower_box_layout(elem, diag, Orientation::Vertical),
112 "FlexboxLayout" => lower_flexbox_layout(elem, diag),
113 "Dialog" => {
114 lower_dialog_layout(elem, style_metrics, diag);
115 return layout_type;
117 }
118 _ => return None,
119 };
120
121 let mut elem = elem.borrow_mut();
122 let elem = &mut *elem;
123 let prev_base = std::mem::replace(&mut elem.base_type, type_register.empty_type());
124 elem.default_fill_parent = (true, true);
125 for (p, ty) in prev_base.property_list() {
128 if !elem.base_type.lookup_property(&p).is_valid()
129 && !elem.property_declarations.contains_key(&p)
130 {
131 elem.property_declarations.insert(p, ty.into());
132 }
133 }
134
135 layout_type
136}
137
138#[derive(Debug, PartialEq, Eq)]
140enum RowColExpressionType {
141 Auto, Literal,
143 RuntimeExpression,
144}
145impl RowColExpressionType {
146 fn from_option_expr(
147 expr: &Option<Expression>,
148 is_number_literal: bool,
149 ) -> RowColExpressionType {
150 match expr {
151 None => RowColExpressionType::Auto,
152 Some(_) if is_number_literal => RowColExpressionType::Literal,
153 Some(_) => RowColExpressionType::RuntimeExpression,
154 }
155 }
156}
157
158fn lower_grid_layout(
159 component: &Rc<Component>,
160 grid_layout_element: &ElementRc,
161 diag: &mut BuildDiagnostics,
162 type_register: &TypeRegister,
163) {
164 let mut grid = GridLayout {
165 elems: Default::default(),
166 geometry: LayoutGeometry::new(grid_layout_element),
167 dialog_button_roles: None,
168 uses_auto: false,
169 };
170
171 let layout_organized_data_prop = create_new_prop(
172 grid_layout_element,
173 SmolStr::new_static("layout-organized-data"),
174 Type::ArrayOfU16,
175 );
176 let layout_cache_prop_h = create_new_prop(
177 grid_layout_element,
178 SmolStr::new_static("layout-cache-h"),
179 Type::LayoutCache,
180 );
181 let layout_cache_prop_v = create_new_prop(
182 grid_layout_element,
183 SmolStr::new_static("layout-cache-v"),
184 Type::LayoutCache,
185 );
186 let layout_info_prop_h = create_new_prop(
187 grid_layout_element,
188 SmolStr::new_static("layoutinfo-h"),
189 layout_info_type().into(),
190 );
191 let layout_info_prop_v = create_new_prop(
192 grid_layout_element,
193 SmolStr::new_static("layoutinfo-v"),
194 layout_info_type().into(),
195 );
196
197 let layout_children = std::mem::take(&mut grid_layout_element.borrow_mut().children);
198 let mut collected_children = Vec::new();
199 let mut new_row = false; let mut numbering_type: Option<RowColExpressionType> = None;
201 let mut num_cached_items: usize = 0;
202 for layout_child in layout_children {
203 let is_repeated_row = {
204 if layout_child.borrow().repeated.is_some()
205 && let ElementType::Component(comp) = &layout_child.borrow().base_type
206 {
207 match &comp.root_element.borrow().base_type {
208 ElementType::Builtin(b) => b.name == "Row",
209 _ => false,
210 }
211 } else {
212 false
213 }
214 };
215 if is_repeated_row {
216 grid.add_repeated_row(
217 &layout_child,
218 &layout_cache_prop_h,
219 &layout_cache_prop_v,
220 &layout_organized_data_prop,
221 diag,
222 &mut num_cached_items,
223 );
224 collected_children.push(layout_child);
225 new_row = true;
226 } else if layout_child.borrow().base_type.type_name() == Some("Row") {
227 new_row = true;
228 let row_children = std::mem::take(&mut layout_child.borrow_mut().children);
229 for row_child in row_children {
230 if let Some(binding) = row_child.borrow_mut().bindings.get("row") {
231 diag.push_warning(
232 "The 'row' property cannot be used for elements inside a Row. This was accepted by previous versions of Slint, but may become an error in the future".to_string(),
233 &*binding.borrow(),
234 );
235 }
236 grid.add_element(
237 &row_child,
238 new_row,
239 &layout_cache_prop_h,
240 &layout_cache_prop_v,
241 &layout_organized_data_prop,
242 &mut numbering_type,
243 diag,
244 &mut num_cached_items,
245 );
246 collected_children.push(row_child);
247 new_row = false;
248 }
249 new_row = true; if layout_child.borrow().has_popup_child {
251 layout_child.borrow_mut().base_type = type_register.empty_type();
253 collected_children.push(layout_child);
254 } else {
255 component.optimized_elements.borrow_mut().push(layout_child);
256 }
257 } else {
258 grid.add_element(
259 &layout_child,
260 new_row,
261 &layout_cache_prop_h,
262 &layout_cache_prop_v,
263 &layout_organized_data_prop,
264 &mut numbering_type,
265 diag,
266 &mut num_cached_items,
267 );
268 collected_children.push(layout_child);
269 new_row = false;
270 }
271 }
272 grid_layout_element.borrow_mut().children = collected_children;
273 grid.uses_auto = numbering_type == Some(RowColExpressionType::Auto);
274 let span = grid_layout_element.borrow().to_source_location();
275
276 layout_organized_data_prop.element().borrow_mut().bindings.insert(
277 layout_organized_data_prop.name().clone(),
278 BindingExpression::new_with_span(
279 Expression::OrganizeGridLayout(grid.clone()),
280 span.clone(),
281 )
282 .into(),
283 );
284 layout_cache_prop_h.element().borrow_mut().bindings.insert(
285 layout_cache_prop_h.name().clone(),
286 BindingExpression::new_with_span(
287 Expression::SolveGridLayout {
288 layout_organized_data_prop: layout_organized_data_prop.clone(),
289 layout: grid.clone(),
290 orientation: Orientation::Horizontal,
291 },
292 span.clone(),
293 )
294 .into(),
295 );
296 layout_cache_prop_v.element().borrow_mut().bindings.insert(
297 layout_cache_prop_v.name().clone(),
298 BindingExpression::new_with_span(
299 Expression::SolveGridLayout {
300 layout_organized_data_prop: layout_organized_data_prop.clone(),
301 layout: grid.clone(),
302 orientation: Orientation::Vertical,
303 },
304 span.clone(),
305 )
306 .into(),
307 );
308 layout_info_prop_h.element().borrow_mut().bindings.insert(
309 layout_info_prop_h.name().clone(),
310 BindingExpression::new_with_span(
311 Expression::ComputeGridLayoutInfo {
312 layout_organized_data_prop: layout_organized_data_prop.clone(),
313 layout: grid.clone(),
314 orientation: Orientation::Horizontal,
315 },
316 span.clone(),
317 )
318 .into(),
319 );
320 layout_info_prop_v.element().borrow_mut().bindings.insert(
321 layout_info_prop_v.name().clone(),
322 BindingExpression::new_with_span(
323 Expression::ComputeGridLayoutInfo {
324 layout_organized_data_prop: layout_organized_data_prop.clone(),
325 layout: grid.clone(),
326 orientation: Orientation::Vertical,
327 },
328 span,
329 )
330 .into(),
331 );
332 grid_layout_element.borrow_mut().layout_info_prop =
333 Some((layout_info_prop_h, layout_info_prop_v));
334 for d in grid_layout_element.borrow_mut().debug.iter_mut() {
335 d.layout = Some(Layout::GridLayout(grid.clone()));
336 }
337}
338
339impl GridLayout {
340 fn add_element(
341 &mut self,
342 item_element: &ElementRc,
343 new_row: bool,
344 layout_cache_prop_h: &NamedReference,
345 layout_cache_prop_v: &NamedReference,
346 organized_data_prop: &NamedReference,
347 numbering_type: &mut Option<RowColExpressionType>,
348 diag: &mut BuildDiagnostics,
349 num_cached_items: &mut usize,
350 ) {
351 {
353 let mut check_expr = |name: &str| {
354 let mut is_number_literal = false;
355 let expr = item_element.borrow_mut().bindings.get(name).map(|e| {
356 let expr = &e.borrow().expression;
357 is_number_literal =
358 check_number_literal_is_positive_integer(expr, name, &*e.borrow(), diag);
359 expr.clone()
360 });
361 (expr, is_number_literal)
362 };
363
364 let (row_expr, row_is_number_literal) = check_expr("row");
365 let (col_expr, col_is_number_literal) = check_expr("col");
366 check_expr("rowspan");
367 check_expr("colspan");
368
369 let mut check_numbering_consistency =
370 |expr_type: RowColExpressionType, prop_name: &str| {
371 if !matches!(expr_type, RowColExpressionType::Literal) {
372 if let Some(current_numbering_type) = numbering_type {
373 if *current_numbering_type != expr_type {
374 let element_ref = item_element.borrow();
375 let span: &dyn Spanned =
376 if let Some(binding) = element_ref.bindings.get(prop_name) {
377 &*binding.borrow()
378 } else {
379 &*element_ref
380 };
381 diag.push_error(
382 format!("Cannot mix auto-numbering and runtime expressions for the '{prop_name}' property"),
383 span,
384 );
385 }
386 } else {
387 *numbering_type = Some(expr_type);
389 }
390 }
391 };
392
393 let row_expr_type =
394 RowColExpressionType::from_option_expr(&row_expr, row_is_number_literal);
395 check_numbering_consistency(row_expr_type, "row");
396
397 let col_expr_type =
398 RowColExpressionType::from_option_expr(&col_expr, col_is_number_literal);
399 check_numbering_consistency(col_expr_type, "col");
400 }
401
402 let propref = |name: &'static str| -> Option<RowColExpr> {
403 let nr = crate::layout::binding_reference(item_element, name).map(|nr| {
404 let e = nr.element();
406 let mut nr = nr.clone();
407 if e.borrow().repeated.is_some()
408 && let crate::langtype::ElementType::Component(c) = e.borrow().base_type.clone()
409 {
410 nr = NamedReference::new(&c.root_element, nr.name().clone())
411 };
412 nr
413 });
414 nr.map(RowColExpr::Named)
415 };
416
417 let row_expr = propref("row");
418 let col_expr = propref("col");
419 let rowspan_expr = propref("rowspan");
420 let colspan_expr = propref("colspan");
421
422 self.add_element_with_coord_as_expr(
423 item_element,
424 new_row,
425 (&row_expr, &col_expr),
426 (&rowspan_expr, &colspan_expr),
427 layout_cache_prop_h,
428 layout_cache_prop_v,
429 organized_data_prop,
430 diag,
431 num_cached_items,
432 );
433 }
434
435 fn add_element_with_coord(
436 &mut self,
437 item_element: &ElementRc,
438 (row, col): (u16, u16),
439 (rowspan, colspan): (u16, u16),
440 layout_cache_prop_h: &NamedReference,
441 layout_cache_prop_v: &NamedReference,
442 organized_data_prop: &NamedReference,
443 diag: &mut BuildDiagnostics,
444 num_cached_items: &mut usize,
445 ) {
446 self.add_element_with_coord_as_expr(
447 item_element,
448 false, (&Some(RowColExpr::Literal(row)), &Some(RowColExpr::Literal(col))),
450 (&Some(RowColExpr::Literal(rowspan)), &Some(RowColExpr::Literal(colspan))),
451 layout_cache_prop_h,
452 layout_cache_prop_v,
453 organized_data_prop,
454 diag,
455 num_cached_items,
456 )
457 }
458
459 fn add_repeated_row(
460 &mut self,
461 item_element: &ElementRc,
462 layout_cache_prop_h: &NamedReference,
463 layout_cache_prop_v: &NamedReference,
464 organized_data_prop: &NamedReference,
465 diag: &mut BuildDiagnostics,
466 num_cached_items: &mut usize,
467 ) {
468 let layout_item = create_layout_item(item_element, diag);
469 if let ElementType::Component(comp) = &item_element.borrow().base_type {
470 let mut children_layout_items = Vec::new();
471 let jump_pos = *num_cached_items;
472
473 let children_ref = comp.root_element.borrow().children.clone();
475 let has_inner_repeaters = children_ref.iter().any(|c| c.borrow().repeated.is_some());
476
477 let step = children_ref.len() as f64;
482 let (stride_h_expr, stride_v_expr, stride_org_expr): (
483 Expression,
484 Expression,
485 Expression,
486 ) = if has_inner_repeaters {
487 (
490 Expression::LayoutCacheAccess {
491 layout_cache_prop: layout_cache_prop_h.clone(),
492 index: jump_pos * 2 + 1,
493 repeater_index: None,
494 entries_per_item: 1,
495 },
496 Expression::LayoutCacheAccess {
497 layout_cache_prop: layout_cache_prop_v.clone(),
498 index: jump_pos * 2 + 1,
499 repeater_index: None,
500 entries_per_item: 1,
501 },
502 Expression::LayoutCacheAccess {
503 layout_cache_prop: organized_data_prop.clone(),
504 index: jump_pos * 4 + 1,
505 repeater_index: None,
506 entries_per_item: 1,
507 },
508 )
509 } else {
510 (
512 Expression::NumberLiteral(step * 2.0, Unit::None), Expression::NumberLiteral(step * 2.0, Unit::None), Expression::NumberLiteral(step * 4.0, Unit::None), )
516 };
517
518 let mut cumulative_pos: Option<Expression> = None;
529
530 for child in children_ref.iter() {
531 let is_nested_repeater = child.borrow().repeated.is_some();
532 let sub_item = create_layout_item(child, diag);
533
534 let propref = |name: &'static str, elem: &ElementRc| -> Option<RowColExpr> {
536 let nr = crate::layout::binding_reference(elem, name).map(|nr| {
537 let e = nr.element();
538 let mut nr = nr.clone();
539 if e.borrow().repeated.is_some()
540 && let crate::langtype::ElementType::Component(c) =
541 e.borrow().base_type.clone()
542 {
543 nr = NamedReference::new(&c.root_element, nr.name().clone())
544 };
545 nr
546 });
547 nr.map(RowColExpr::Named)
548 };
549 let colspan_expr = propref("colspan", child);
550 let rowspan_expr = propref("rowspan", child);
551 let child_grid_cell = Rc::new(RefCell::new(GridLayoutCell {
552 new_row: false,
553 col_expr: RowColExpr::Auto,
554 row_expr: RowColExpr::Auto,
555 colspan_expr: colspan_expr.unwrap_or(RowColExpr::Literal(1)),
556 rowspan_expr: rowspan_expr.unwrap_or(RowColExpr::Literal(1)),
557 child_items: None,
558 }));
559 child.borrow_mut().grid_layout_cell = Some(child_grid_cell);
560
561 let effective_inner_rep_idx = if is_nested_repeater {
567 let model_idx = sub_item.repeater_index.clone().unwrap();
569 Some(if let Some(ref base) = cumulative_pos {
570 Expression::BinaryExpression {
571 lhs: Box::new(base.clone()),
572 rhs: Box::new(model_idx),
573 op: '+',
574 }
575 } else {
576 model_idx
577 })
578 } else {
579 cumulative_pos.clone()
581 };
582
583 let repeater_params = RepeaterCacheParams {
584 index: jump_pos,
585 rep_idx: &layout_item.repeater_index,
586 child_offset: 0,
587 inner_rep_idx: &effective_inner_rep_idx,
588 };
589 set_coord_prop_from_cache(
591 &sub_item.elem,
592 &sub_item.item.constraints,
593 layout_cache_prop_h,
594 layout_cache_prop_v,
595 &repeater_params,
596 Some(&stride_h_expr),
597 Some(&stride_v_expr),
598 diag,
599 );
600 set_grid_rowcol_from_cache(
602 &sub_item.elem,
603 organized_data_prop,
604 &repeater_params,
605 Some(&stride_org_expr),
606 (&None::<RowColExpr>, &None::<RowColExpr>),
607 diag,
608 );
609
610 if is_nested_repeater {
612 let (model_expr, is_conditional) = {
616 let b = child.borrow();
617 let r = b.repeated.as_ref().unwrap();
618 (r.model.clone(), r.is_conditional_element)
619 };
620 let len_expr = if is_conditional {
621 Expression::Condition {
622 condition: Box::new(model_expr),
623 true_expr: Box::new(Expression::NumberLiteral(1., Unit::None)),
624 false_expr: Box::new(Expression::NumberLiteral(0., Unit::None)),
625 }
626 } else {
627 Expression::FunctionCall {
628 function: Callable::Builtin(BuiltinFunction::ArrayLength),
629 arguments: vec![model_expr],
630 source_location: None,
631 }
632 };
633 cumulative_pos = Some(if let Some(prev) = cumulative_pos.take() {
634 Expression::BinaryExpression {
635 lhs: Box::new(prev),
636 rhs: Box::new(len_expr),
637 op: '+',
638 }
639 } else {
640 len_expr
641 });
642 } else {
643 cumulative_pos = Some(if let Some(prev) = cumulative_pos.take() {
645 Expression::BinaryExpression {
646 lhs: Box::new(prev),
647 rhs: Box::new(Expression::NumberLiteral(1., Unit::None)),
648 op: '+',
649 }
650 } else {
651 Expression::NumberLiteral(1., Unit::None)
652 });
653 }
654
655 if is_nested_repeater {
656 children_layout_items.push(RowChildTemplate::Repeated {
657 item: sub_item.item,
658 repeated_element: child.clone(),
659 });
660 } else {
661 children_layout_items.push(RowChildTemplate::Static(sub_item.item));
662 }
663 }
664
665 *num_cached_items += 1;
667 let grid_layout_cell = Rc::new(RefCell::new(GridLayoutCell {
669 new_row: true,
670 col_expr: RowColExpr::Auto,
671 row_expr: RowColExpr::Auto,
672 colspan_expr: RowColExpr::Literal(1),
673 rowspan_expr: RowColExpr::Literal(1),
674 child_items: Some(children_layout_items),
675 }));
676 let grid_layout_element = GridLayoutElement {
677 cell: grid_layout_cell.clone(),
678 item: layout_item.item.clone(),
679 };
680 comp.root_element.borrow_mut().grid_layout_cell = Some(grid_layout_cell);
681 self.elems.push(grid_layout_element);
682 }
683 }
684
685 fn add_element_with_coord_as_expr(
686 &mut self,
687 item_element: &ElementRc,
688 new_row: bool,
689 (row_expr, col_expr): (&Option<RowColExpr>, &Option<RowColExpr>),
690 (rowspan_expr, colspan_expr): (&Option<RowColExpr>, &Option<RowColExpr>),
691 layout_cache_prop_h: &NamedReference,
692 layout_cache_prop_v: &NamedReference,
693 organized_data_prop: &NamedReference,
694 diag: &mut BuildDiagnostics,
695 num_cached_items: &mut usize,
696 ) {
697 let layout_item = create_layout_item(item_element, diag);
698
699 let has_repeater_indirection = layout_item.repeater_index.is_some();
700 let stride_coord =
702 has_repeater_indirection.then(|| Expression::NumberLiteral(2.0, Unit::None));
703 let stride_org =
704 has_repeater_indirection.then(|| Expression::NumberLiteral(4.0, Unit::None));
705 let repeater_params = RepeaterCacheParams {
706 index: *num_cached_items,
707 rep_idx: &layout_item.repeater_index,
708 child_offset: 0,
709 inner_rep_idx: &None,
710 };
711 set_coord_prop_from_cache(
712 &layout_item.elem,
713 &layout_item.item.constraints,
714 layout_cache_prop_h,
715 layout_cache_prop_v,
716 &repeater_params,
717 stride_coord.as_ref(),
718 stride_coord.as_ref(),
719 diag,
720 );
721 set_grid_rowcol_from_cache(
722 &layout_item.elem,
723 organized_data_prop,
724 &repeater_params,
725 stride_org.as_ref(),
726 (row_expr, col_expr),
727 diag,
728 );
729
730 let expr_or_default = |expr: &Option<RowColExpr>, default: RowColExpr| -> RowColExpr {
731 match expr {
732 Some(RowColExpr::Literal(v)) => RowColExpr::Literal(*v),
733 Some(RowColExpr::Named(nr)) => RowColExpr::Named(nr.clone()),
734 Some(RowColExpr::Auto) => RowColExpr::Auto,
735 None => default,
736 }
737 };
738
739 let grid_layout_cell = Rc::new(RefCell::new(GridLayoutCell {
740 new_row,
741 col_expr: expr_or_default(col_expr, RowColExpr::Auto),
742 row_expr: expr_or_default(row_expr, RowColExpr::Auto),
743 colspan_expr: expr_or_default(colspan_expr, RowColExpr::Literal(1)),
744 rowspan_expr: expr_or_default(rowspan_expr, RowColExpr::Literal(1)),
745 child_items: None,
746 }));
747 let grid_layout_element =
748 GridLayoutElement { cell: grid_layout_cell.clone(), item: layout_item.item.clone() };
749 layout_item.elem.borrow_mut().grid_layout_cell = Some(grid_layout_cell);
750 self.elems.push(grid_layout_element);
751 *num_cached_items += 1;
752 }
753}
754
755fn lower_box_layout(
756 layout_element: &ElementRc,
757 diag: &mut BuildDiagnostics,
758 orientation: Orientation,
759) {
760 let mut layout = BoxLayout {
761 orientation,
762 elems: Default::default(),
763 geometry: LayoutGeometry::new(layout_element),
764 };
765
766 let layout_cache_prop =
767 create_new_prop(layout_element, SmolStr::new_static("layout-cache"), Type::LayoutCache);
768 let layout_info_prop_v = create_new_prop(
769 layout_element,
770 SmolStr::new_static("layoutinfo-v"),
771 layout_info_type().into(),
772 );
773 let layout_info_prop_h = create_new_prop(
774 layout_element,
775 SmolStr::new_static("layoutinfo-h"),
776 layout_info_type().into(),
777 );
778
779 let layout_children = std::mem::take(&mut layout_element.borrow_mut().children);
780
781 let (begin_padding, end_padding) = match orientation {
782 Orientation::Horizontal => (&layout.geometry.padding.top, &layout.geometry.padding.bottom),
783 Orientation::Vertical => (&layout.geometry.padding.left, &layout.geometry.padding.right),
784 };
785 let (pos, size, pad, ortho) = match orientation {
786 Orientation::Horizontal => ("x", "width", "y", "height"),
787 Orientation::Vertical => ("y", "height", "x", "width"),
788 };
789 let pad_expr = begin_padding.clone().map(Expression::PropertyReference);
790 let mut size_expr = Expression::PropertyReference(NamedReference::new(
791 layout_element,
792 SmolStr::new_static(ortho),
793 ));
794 if let Some(p) = begin_padding {
795 size_expr = Expression::BinaryExpression {
796 lhs: Box::new(std::mem::take(&mut size_expr)),
797 rhs: Box::new(Expression::PropertyReference(p.clone())),
798 op: '-',
799 }
800 }
801 if let Some(p) = end_padding {
802 size_expr = Expression::BinaryExpression {
803 lhs: Box::new(std::mem::take(&mut size_expr)),
804 rhs: Box::new(Expression::PropertyReference(p.clone())),
805 op: '-',
806 }
807 }
808
809 for layout_child in &layout_children {
810 let item = create_layout_item(layout_child, diag);
811 let index = layout.elems.len() * 2;
812 let rep_idx = &item.repeater_index;
813 let (fixed_size, fixed_ortho) = match orientation {
814 Orientation::Horizontal => {
815 (item.item.constraints.fixed_width, item.item.constraints.fixed_height)
816 }
817 Orientation::Vertical => {
818 (item.item.constraints.fixed_height, item.item.constraints.fixed_width)
819 }
820 };
821 let actual_elem = &item.elem;
822 set_prop_from_cache(actual_elem, pos, &layout_cache_prop, index, rep_idx, 2, diag);
824 if !fixed_size {
825 set_prop_from_cache(actual_elem, size, &layout_cache_prop, index + 1, rep_idx, 2, diag);
826 }
827 if let Some(pad_expr) = pad_expr.clone() {
828 actual_elem.borrow_mut().bindings.insert(pad.into(), RefCell::new(pad_expr.into()));
829 }
830 if !fixed_ortho {
831 actual_elem
832 .borrow_mut()
833 .bindings
834 .insert(ortho.into(), RefCell::new(size_expr.clone().into()));
835 }
836 layout.elems.push(item.item);
837 }
838 layout_element.borrow_mut().children = layout_children;
839 let span = layout_element.borrow().to_source_location();
840 layout_cache_prop.element().borrow_mut().bindings.insert(
841 layout_cache_prop.name().clone(),
842 BindingExpression::new_with_span(
843 Expression::SolveBoxLayout(layout.clone(), orientation),
844 span.clone(),
845 )
846 .into(),
847 );
848 layout_info_prop_h.element().borrow_mut().bindings.insert(
849 layout_info_prop_h.name().clone(),
850 BindingExpression::new_with_span(
851 Expression::ComputeBoxLayoutInfo(layout.clone(), Orientation::Horizontal),
852 span.clone(),
853 )
854 .into(),
855 );
856 layout_info_prop_v.element().borrow_mut().bindings.insert(
857 layout_info_prop_v.name().clone(),
858 BindingExpression::new_with_span(
859 Expression::ComputeBoxLayoutInfo(layout.clone(), Orientation::Vertical),
860 span,
861 )
862 .into(),
863 );
864 layout_element.borrow_mut().layout_info_prop = Some((layout_info_prop_h, layout_info_prop_v));
865 for d in layout_element.borrow_mut().debug.iter_mut() {
866 d.layout = Some(Layout::BoxLayout(layout.clone()));
867 }
868}
869
870fn lower_flexbox_layout(layout_element: &ElementRc, diag: &mut BuildDiagnostics) {
871 if let Some(binding) = layout_element.borrow().bindings.get("alignment") {
874 let binding = binding.borrow();
875 if matches!(binding.expression.ignore_debug_hooks(),
876 Expression::EnumerationValue(v) if v.enumeration.name == "LayoutAlignment"
877 && v.enumeration.values[v.value] == "stretch")
878 {
879 diag.push_warning(
880 "alignment: stretch has no effect on FlexboxLayout".into(),
881 &*binding,
882 );
883 }
884 }
885
886 let direction = crate::layout::binding_reference(layout_element, "flex-direction");
887 let align_content = crate::layout::binding_reference(layout_element, "align-content");
888 let align_items = crate::layout::binding_reference(layout_element, "align-items");
889 let flex_wrap = crate::layout::binding_reference(layout_element, "flex-wrap");
890
891 let mut layout = crate::layout::FlexboxLayout {
892 elems: Default::default(),
893 geometry: LayoutGeometry::new(layout_element),
894 direction,
895 align_content,
896 align_items,
897 flex_wrap,
898 };
899
900 let layout_cache_prop =
902 create_new_prop(layout_element, SmolStr::new_static("layout-cache"), Type::LayoutCache);
903 let layout_info_prop_v = create_new_prop(
904 layout_element,
905 SmolStr::new_static("layoutinfo-v"),
906 layout_info_type().into(),
907 );
908 let layout_info_prop_h = create_new_prop(
909 layout_element,
910 SmolStr::new_static("layoutinfo-h"),
911 layout_info_type().into(),
912 );
913
914 let layout_children = std::mem::take(&mut layout_element.borrow_mut().children);
915
916 for layout_child in &layout_children {
917 let item = create_layout_item(layout_child, diag);
918 let index = layout.elems.len() * 4; let rep_idx = &item.repeater_index;
920 let actual_elem = &item.elem;
921
922 set_prop_from_cache(actual_elem, "x", &layout_cache_prop, index, rep_idx, 4, diag);
924 set_prop_from_cache(actual_elem, "y", &layout_cache_prop, index + 1, rep_idx, 4, diag);
926 if !item.item.constraints.fixed_width {
928 set_prop_from_cache(
929 actual_elem,
930 "width",
931 &layout_cache_prop,
932 index + 2,
933 rep_idx,
934 4,
935 diag,
936 );
937 }
938 if !item.item.constraints.fixed_height {
940 set_prop_from_cache(
941 actual_elem,
942 "height",
943 &layout_cache_prop,
944 index + 3,
945 rep_idx,
946 4,
947 diag,
948 );
949 }
950 let flex_grow = crate::layout::binding_reference(actual_elem, "flex-grow");
951 let flex_shrink = crate::layout::binding_reference(actual_elem, "flex-shrink");
952 let flex_basis = crate::layout::binding_reference(actual_elem, "flex-basis");
953 let align_self = crate::layout::binding_reference(actual_elem, "flex-align-self");
954 let order = crate::layout::binding_reference(actual_elem, "flex-order");
955 layout.elems.push(crate::layout::FlexboxLayoutItem {
956 item: item.item,
957 flex_grow,
958 flex_shrink,
959 flex_basis,
960 align_self,
961 order,
962 });
963 }
964 layout_element.borrow_mut().children = layout_children;
965 let span = layout_element.borrow().to_source_location();
966
967 layout_cache_prop.element().borrow_mut().bindings.insert(
968 layout_cache_prop.name().clone(),
969 BindingExpression::new_with_span(
970 Expression::SolveFlexboxLayout(layout.clone()),
971 span.clone(),
972 )
973 .into(),
974 );
975 layout_info_prop_h.element().borrow_mut().bindings.insert(
976 layout_info_prop_h.name().clone(),
977 BindingExpression::new_with_span(
978 Expression::ComputeFlexboxLayoutInfo(layout.clone(), Orientation::Horizontal),
979 span.clone(),
980 )
981 .into(),
982 );
983 layout_info_prop_v.element().borrow_mut().bindings.insert(
984 layout_info_prop_v.name().clone(),
985 BindingExpression::new_with_span(
986 Expression::ComputeFlexboxLayoutInfo(layout.clone(), Orientation::Vertical),
987 span,
988 )
989 .into(),
990 );
991 layout_element.borrow_mut().layout_info_prop = Some((layout_info_prop_h, layout_info_prop_v));
992 for d in layout_element.borrow_mut().debug.iter_mut() {
993 d.layout = Some(Layout::FlexboxLayout(layout.clone()));
994 }
995}
996
997fn lower_dialog_layout(
998 dialog_element: &ElementRc,
999 style_metrics: &Rc<Component>,
1000 diag: &mut BuildDiagnostics,
1001) {
1002 let mut grid = GridLayout {
1003 elems: Default::default(),
1004 geometry: LayoutGeometry::new(dialog_element),
1005 dialog_button_roles: None,
1006 uses_auto: true,
1007 };
1008 let metrics = &style_metrics.root_element;
1009 grid.geometry
1010 .padding
1011 .bottom
1012 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-padding")));
1013 grid.geometry
1014 .padding
1015 .top
1016 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-padding")));
1017 grid.geometry
1018 .padding
1019 .left
1020 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-padding")));
1021 grid.geometry
1022 .padding
1023 .right
1024 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-padding")));
1025 grid.geometry
1026 .spacing
1027 .horizontal
1028 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-spacing")));
1029 grid.geometry
1030 .spacing
1031 .vertical
1032 .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-spacing")));
1033
1034 let layout_organized_data_prop = create_new_prop(
1035 dialog_element,
1036 SmolStr::new_static("layout-organized-data"),
1037 Type::ArrayOfU16,
1038 );
1039 let layout_cache_prop_h =
1040 create_new_prop(dialog_element, SmolStr::new_static("layout-cache-h"), Type::LayoutCache);
1041 let layout_cache_prop_v =
1042 create_new_prop(dialog_element, SmolStr::new_static("layout-cache-v"), Type::LayoutCache);
1043 let layout_info_prop_h = create_new_prop(
1044 dialog_element,
1045 SmolStr::new_static("layoutinfo-h"),
1046 layout_info_type().into(),
1047 );
1048 let layout_info_prop_v = create_new_prop(
1049 dialog_element,
1050 SmolStr::new_static("layoutinfo-v"),
1051 layout_info_type().into(),
1052 );
1053
1054 let mut main_widget = None;
1055 let mut button_roles = Vec::new();
1056 let mut seen_buttons = HashSet::new();
1057 let mut num_cached_items: usize = 0;
1058 let layout_children = std::mem::take(&mut dialog_element.borrow_mut().children);
1059 for layout_child in &layout_children {
1060 let dialog_button_role_binding =
1061 layout_child.borrow_mut().bindings.remove("dialog-button-role");
1062 let is_button = if let Some(role_binding) = dialog_button_role_binding {
1063 let role_binding = role_binding.into_inner();
1064 if let Expression::EnumerationValue(val) =
1065 super::ignore_debug_hooks(&role_binding.expression)
1066 {
1067 let en = &val.enumeration;
1068 debug_assert_eq!(en.name, "DialogButtonRole");
1069 button_roles.push(en.values[val.value].clone());
1070 if val.value == 0 {
1071 diag.push_error(
1072 "The `dialog-button-role` cannot be set explicitly to none".into(),
1073 &role_binding,
1074 );
1075 }
1076 } else {
1077 diag.push_error(
1078 "The `dialog-button-role` property must be known at compile-time".into(),
1079 &role_binding,
1080 );
1081 }
1082 true
1083 } else if matches!(&layout_child.borrow().lookup_property("kind").property_type, Type::Enumeration(e) if e.name == "StandardButtonKind")
1084 {
1085 match layout_child.borrow().bindings.get("kind") {
1087 None => diag.push_error(
1088 "The `kind` property of the StandardButton in a Dialog must be set".into(),
1089 &*layout_child.borrow(),
1090 ),
1091 Some(binding) => {
1092 let binding = &*binding.borrow();
1093 if let Expression::EnumerationValue(val) =
1094 super::ignore_debug_hooks(&binding.expression)
1095 {
1096 let en = &val.enumeration;
1097 debug_assert_eq!(en.name, "StandardButtonKind");
1098 let kind = &en.values[val.value];
1099 let role = match kind.as_str() {
1100 "ok" => "accept",
1101 "cancel" => "reject",
1102 "apply" => "apply",
1103 "close" => "reject",
1104 "reset" => "reset",
1105 "help" => "help",
1106 "yes" => "accept",
1107 "no" => "reject",
1108 "abort" => "reject",
1109 "retry" => "accept",
1110 "ignore" => "accept",
1111 _ => unreachable!(),
1112 };
1113 button_roles.push(role.into());
1114 if !seen_buttons.insert(val.value) {
1115 diag.push_error("Duplicated `kind`: There are two StandardButton in this Dialog with the same kind".into(), binding);
1116 } else if Rc::ptr_eq(
1117 dialog_element,
1118 &dialog_element
1119 .borrow()
1120 .enclosing_component
1121 .upgrade()
1122 .unwrap()
1123 .root_element,
1124 ) {
1125 let clicked_ty =
1126 layout_child.borrow().lookup_property("clicked").property_type;
1127 if matches!(&clicked_ty, Type::Callback { .. })
1128 && layout_child.borrow().bindings.get("clicked").is_none_or(|c| {
1129 matches!(c.borrow().expression, Expression::Invalid)
1130 })
1131 {
1132 dialog_element
1133 .borrow_mut()
1134 .property_declarations
1135 .entry(format_smolstr!("{}-clicked", kind))
1136 .or_insert_with(|| PropertyDeclaration {
1137 property_type: clicked_ty,
1138 node: None,
1139 expose_in_public_api: true,
1140 is_alias: Some(NamedReference::new(
1141 layout_child,
1142 SmolStr::new_static("clicked"),
1143 )),
1144 visibility: PropertyVisibility::InOut,
1145 pure: None,
1146 });
1147 }
1148 }
1149 } else {
1150 diag.push_error(
1151 "The `kind` property of the StandardButton in a Dialog must be known at compile-time"
1152 .into(),
1153 binding,
1154 );
1155 }
1156 }
1157 }
1158 true
1159 } else {
1160 false
1161 };
1162
1163 if is_button {
1164 grid.add_element_with_coord(
1165 layout_child,
1166 (1, button_roles.len() as u16),
1167 (1, 1),
1168 &layout_cache_prop_h,
1169 &layout_cache_prop_v,
1170 &layout_organized_data_prop,
1171 diag,
1172 &mut num_cached_items,
1173 );
1174 } else if main_widget.is_some() {
1175 diag.push_error(
1176 "A Dialog can have only one child element that is not a StandardButton".into(),
1177 &*layout_child.borrow(),
1178 );
1179 } else {
1180 main_widget = Some(layout_child.clone())
1181 }
1182 }
1183 dialog_element.borrow_mut().children = layout_children;
1184
1185 if let Some(main_widget) = main_widget {
1186 grid.add_element_with_coord(
1187 &main_widget,
1188 (0, 0),
1189 (1, button_roles.len() as u16 + 1),
1190 &layout_cache_prop_h,
1191 &layout_cache_prop_v,
1192 &layout_organized_data_prop,
1193 diag,
1194 &mut num_cached_items,
1195 );
1196 } else {
1197 diag.push_error(
1198 "A Dialog must have a single child element that is not StandardButton".into(),
1199 &*dialog_element.borrow(),
1200 );
1201 }
1202 grid.dialog_button_roles = Some(button_roles);
1203
1204 let span = dialog_element.borrow().to_source_location();
1205 layout_organized_data_prop.element().borrow_mut().bindings.insert(
1206 layout_organized_data_prop.name().clone(),
1207 BindingExpression::new_with_span(
1208 Expression::OrganizeGridLayout(grid.clone()),
1209 span.clone(),
1210 )
1211 .into(),
1212 );
1213 layout_cache_prop_h.element().borrow_mut().bindings.insert(
1214 layout_cache_prop_h.name().clone(),
1215 BindingExpression::new_with_span(
1216 Expression::SolveGridLayout {
1217 layout_organized_data_prop: layout_organized_data_prop.clone(),
1218 layout: grid.clone(),
1219 orientation: Orientation::Horizontal,
1220 },
1221 span.clone(),
1222 )
1223 .into(),
1224 );
1225 layout_cache_prop_v.element().borrow_mut().bindings.insert(
1226 layout_cache_prop_v.name().clone(),
1227 BindingExpression::new_with_span(
1228 Expression::SolveGridLayout {
1229 layout_organized_data_prop: layout_organized_data_prop.clone(),
1230 layout: grid.clone(),
1231 orientation: Orientation::Vertical,
1232 },
1233 span.clone(),
1234 )
1235 .into(),
1236 );
1237 layout_info_prop_h.element().borrow_mut().bindings.insert(
1238 layout_info_prop_h.name().clone(),
1239 BindingExpression::new_with_span(
1240 Expression::ComputeGridLayoutInfo {
1241 layout_organized_data_prop: layout_organized_data_prop.clone(),
1242 layout: grid.clone(),
1243 orientation: Orientation::Horizontal,
1244 },
1245 span.clone(),
1246 )
1247 .into(),
1248 );
1249 layout_info_prop_v.element().borrow_mut().bindings.insert(
1250 layout_info_prop_v.name().clone(),
1251 BindingExpression::new_with_span(
1252 Expression::ComputeGridLayoutInfo {
1253 layout_organized_data_prop: layout_organized_data_prop.clone(),
1254 layout: grid.clone(),
1255 orientation: Orientation::Vertical,
1256 },
1257 span,
1258 )
1259 .into(),
1260 );
1261 dialog_element.borrow_mut().layout_info_prop = Some((layout_info_prop_h, layout_info_prop_v));
1262 for d in dialog_element.borrow_mut().debug.iter_mut() {
1263 d.layout = Some(Layout::GridLayout(grid.clone()));
1264 }
1265}
1266
1267struct CreateLayoutItemResult {
1268 item: LayoutItem,
1269 elem: ElementRc,
1270 repeater_index: Option<Expression>,
1271}
1272
1273fn create_layout_item(
1275 item_element: &ElementRc,
1276 diag: &mut BuildDiagnostics,
1277) -> CreateLayoutItemResult {
1278 let fix_explicit_percent = |prop: &str, item: &ElementRc| {
1279 if !item.borrow().bindings.get(prop).is_some_and(|b| b.borrow().ty() == Type::Percent) {
1280 return;
1281 }
1282 let min_name = format_smolstr!("min-{}", prop);
1283 let max_name = format_smolstr!("max-{}", prop);
1284 let mut min_ref = BindingExpression::from(Expression::PropertyReference(
1285 NamedReference::new(item, min_name.clone()),
1286 ));
1287 let mut item = item.borrow_mut();
1288 let b = item.bindings.remove(prop).unwrap().into_inner();
1289 min_ref.span = b.span.clone();
1290 min_ref.priority = b.priority;
1291 item.bindings.insert(max_name.clone(), min_ref.into());
1292 item.bindings.insert(min_name.clone(), b.into());
1293 item.property_declarations.insert(
1294 min_name,
1295 PropertyDeclaration { property_type: Type::Percent, ..PropertyDeclaration::default() },
1296 );
1297 item.property_declarations.insert(
1298 max_name,
1299 PropertyDeclaration { property_type: Type::Percent, ..PropertyDeclaration::default() },
1300 );
1301 };
1302 fix_explicit_percent("width", item_element);
1303 fix_explicit_percent("height", item_element);
1304
1305 item_element.borrow_mut().child_of_layout = true;
1306 let (repeater_index, actual_elem) = if let Some(r) = &item_element.borrow().repeated {
1307 let rep_comp = item_element.borrow().base_type.as_component().clone();
1308 fix_explicit_percent("width", &rep_comp.root_element);
1309 fix_explicit_percent("height", &rep_comp.root_element);
1310
1311 *rep_comp.root_constraints.borrow_mut() =
1312 LayoutConstraints::new(&rep_comp.root_element, diag, DiagnosticLevel::Error);
1313 rep_comp.root_element.borrow_mut().child_of_layout = true;
1314 (
1315 Some(if r.is_conditional_element {
1316 Expression::NumberLiteral(0., Unit::None)
1317 } else {
1318 Expression::RepeaterIndexReference { element: Rc::downgrade(item_element) }
1319 }),
1320 rep_comp.root_element.clone(),
1321 )
1322 } else {
1323 (None, item_element.clone())
1324 };
1325
1326 let constraints = LayoutConstraints::new(&actual_elem, diag, DiagnosticLevel::Error);
1327 CreateLayoutItemResult {
1328 item: LayoutItem { element: item_element.clone(), constraints },
1329 elem: actual_elem,
1330 repeater_index,
1331 }
1332}
1333
1334fn set_grid_prop_from_cache(
1335 elem: &ElementRc,
1336 prop: &str,
1337 layout_cache_prop: &NamedReference,
1338 index: usize,
1339 repeater_index: &Option<Expression>,
1340 child_offset: usize,
1341 stride_expr: Option<&Expression>,
1343 inner_repeater_index: Option<Expression>,
1344 entries_per_item: usize,
1345 diag: &mut BuildDiagnostics,
1346) {
1347 if let Some(stride) = stride_expr {
1348 let repeater_index_boxed = repeater_index.as_ref().map(|x| Box::new(x.clone()));
1350 let expr = Expression::GridRepeaterCacheAccess {
1351 layout_cache_prop: layout_cache_prop.clone(),
1352 index,
1353 repeater_index: repeater_index_boxed.unwrap(),
1354 stride: Box::new(stride.clone()),
1355 child_offset,
1356 inner_repeater_index: inner_repeater_index.map(Box::new),
1357 entries_per_item,
1358 };
1359 insert_cache_prop_binding(expr, elem, prop, layout_cache_prop, diag);
1360 } else {
1361 set_prop_from_cache(
1363 elem,
1364 prop,
1365 layout_cache_prop,
1366 index,
1367 repeater_index,
1368 entries_per_item,
1369 diag,
1370 );
1371 }
1372}
1373
1374fn set_prop_from_cache(
1375 elem: &ElementRc,
1376 prop: &str,
1377 layout_cache_prop: &NamedReference,
1378 index: usize,
1379 repeater_index: &Option<Expression>,
1380 entries_per_item: usize,
1381 diag: &mut BuildDiagnostics,
1382) {
1383 let expr = Expression::LayoutCacheAccess {
1384 layout_cache_prop: layout_cache_prop.clone(),
1385 index,
1386 repeater_index: repeater_index.as_ref().map(|x| Box::new(x.clone())),
1387 entries_per_item,
1388 };
1389 insert_cache_prop_binding(expr, elem, prop, layout_cache_prop, diag);
1390}
1391
1392fn insert_cache_prop_binding(
1393 expr: Expression,
1394 elem: &ElementRc,
1395 prop: &str,
1396 layout_cache_prop: &NamedReference,
1397 diag: &mut BuildDiagnostics,
1398) {
1399 let old = elem.borrow_mut().bindings.insert(
1400 prop.into(),
1401 BindingExpression::new_with_span(
1402 expr,
1403 layout_cache_prop.element().borrow().to_source_location(),
1404 )
1405 .into(),
1406 );
1407 if let Some(old) = old.map(RefCell::into_inner) {
1408 diag.push_error(
1409 format!("The property '{prop}' cannot be set for elements placed in this layout, because the layout is already setting it"),
1410 &old,
1411 );
1412 }
1413}
1414
1415#[derive(Copy, Clone)]
1417struct RepeaterCacheParams<'a> {
1418 index: usize,
1420 rep_idx: &'a Option<Expression>,
1422 child_offset: usize,
1424 inner_rep_idx: &'a Option<Expression>,
1426}
1427
1428fn set_coord_prop_from_cache(
1430 elem: &ElementRc,
1431 constraints: &LayoutConstraints,
1432 layout_cache_prop_h: &NamedReference,
1433 layout_cache_prop_v: &NamedReference,
1434 repeater_params: &RepeaterCacheParams<'_>,
1435 stride_h: Option<&Expression>,
1436 stride_v: Option<&Expression>,
1437 diag: &mut BuildDiagnostics,
1438) {
1439 let has_repeater_indirection = stride_h.is_some();
1440 let cache_idx = repeater_params.index * 2;
1441 let pos_offset = repeater_params.child_offset;
1442 let size_offset = repeater_params.child_offset + 1;
1443 let inner_idx_clone = repeater_params.inner_rep_idx.clone();
1444
1445 let size_cache_idx = if has_repeater_indirection { cache_idx } else { cache_idx + 1 };
1447
1448 set_grid_prop_from_cache(
1449 elem,
1450 "x",
1451 layout_cache_prop_h,
1452 cache_idx,
1453 repeater_params.rep_idx,
1454 pos_offset,
1455 stride_h,
1456 inner_idx_clone.clone(),
1457 2,
1458 diag,
1459 );
1460 if !constraints.fixed_width {
1461 set_grid_prop_from_cache(
1462 elem,
1463 "width",
1464 layout_cache_prop_h,
1465 size_cache_idx,
1466 repeater_params.rep_idx,
1467 size_offset,
1468 stride_h,
1469 inner_idx_clone.clone(),
1470 2,
1471 diag,
1472 );
1473 }
1474 set_grid_prop_from_cache(
1475 elem,
1476 "y",
1477 layout_cache_prop_v,
1478 cache_idx,
1479 repeater_params.rep_idx,
1480 pos_offset,
1481 stride_v,
1482 inner_idx_clone.clone(),
1483 2,
1484 diag,
1485 );
1486 if !constraints.fixed_height {
1487 set_grid_prop_from_cache(
1488 elem,
1489 "height",
1490 layout_cache_prop_v,
1491 size_cache_idx,
1492 repeater_params.rep_idx,
1493 size_offset,
1494 stride_v,
1495 inner_idx_clone,
1496 2,
1497 diag,
1498 );
1499 }
1500}
1501
1502fn set_grid_rowcol_from_cache(
1505 elem: &ElementRc,
1506 organized_data_prop: &NamedReference,
1507 repeater_params: &RepeaterCacheParams<'_>,
1508 stride: Option<&Expression>,
1509 (row_expr, col_expr): (&Option<RowColExpr>, &Option<RowColExpr>),
1510 diag: &mut BuildDiagnostics,
1511) {
1512 let has_repeater_indirection = stride.is_some();
1513 let org_cache_idx = repeater_params.index * 4;
1514
1515 let col_cache_idx = org_cache_idx;
1518 let col_offset = if has_repeater_indirection { repeater_params.child_offset * 4 } else { 0 };
1519
1520 let (row_cache_idx, row_offset) = if has_repeater_indirection {
1521 (org_cache_idx, repeater_params.child_offset * 4 + 2)
1522 } else {
1523 (org_cache_idx + 2, 0)
1524 };
1525
1526 if col_expr.is_none() {
1527 set_grid_prop_from_cache(
1528 elem,
1529 "col",
1530 organized_data_prop,
1531 col_cache_idx,
1532 repeater_params.rep_idx,
1533 col_offset,
1534 stride,
1535 repeater_params.inner_rep_idx.clone(),
1536 4,
1537 diag,
1538 );
1539 }
1540 if row_expr.is_none() {
1541 set_grid_prop_from_cache(
1542 elem,
1543 "row",
1544 organized_data_prop,
1545 row_cache_idx,
1546 repeater_params.rep_idx,
1547 row_offset,
1548 stride,
1549 repeater_params.inner_rep_idx.clone(),
1550 4,
1551 diag,
1552 );
1553 }
1554}
1555
1556fn check_number_literal_is_positive_integer(
1560 expression: &Expression,
1561 name: &str,
1562 span: &dyn crate::diagnostics::Spanned,
1563 diag: &mut BuildDiagnostics,
1564) -> bool {
1565 match super::ignore_debug_hooks(expression) {
1566 Expression::NumberLiteral(v, Unit::None) => {
1567 if *v > u16::MAX as f64 || !v.trunc().approx_eq(v) {
1568 diag.push_error(format!("'{name}' must be a positive integer"), span);
1569 }
1570 true
1571 }
1572 Expression::UnaryOp { op: '-', sub } => {
1573 if let Expression::NumberLiteral(_, Unit::None) = super::ignore_debug_hooks(sub) {
1574 diag.push_error(format!("'{name}' must be a positive integer"), span);
1575 }
1576 true
1577 }
1578 Expression::Cast { from, .. } => {
1579 check_number_literal_is_positive_integer(from, name, span, diag)
1580 }
1581 _ => false,
1582 }
1583}
1584
1585fn recognized_layout_types() -> &'static [&'static str] {
1586 &["Row", "GridLayout", "HorizontalLayout", "VerticalLayout", "FlexboxLayout", "Dialog"]
1587}
1588
1589fn check_no_layout_properties(
1591 item: &ElementRc,
1592 layout_type: &Option<SmolStr>,
1593 parent_layout_type: &Option<SmolStr>,
1594 diag: &mut BuildDiagnostics,
1595) {
1596 let elem = item.borrow();
1597 for (prop, expr) in elem.bindings.iter() {
1598 if !matches!(parent_layout_type.as_deref(), Some("GridLayout") | Some("Row"))
1599 && matches!(prop.as_ref(), "col" | "row" | "colspan" | "rowspan")
1600 {
1601 diag.push_error(format!("{prop} used outside of a GridLayout's cell"), &*expr.borrow());
1602 }
1603 if parent_layout_type.as_deref() != Some("FlexboxLayout")
1604 && matches!(
1605 prop.as_ref(),
1606 "flex-grow" | "flex-shrink" | "flex-basis" | "flex-align-self" | "flex-order"
1607 )
1608 {
1609 diag.push_error(format!("{prop} used outside of a FlexboxLayout"), &*expr.borrow());
1610 }
1611 if parent_layout_type.as_deref() != Some("Dialog")
1612 && matches!(prop.as_ref(), "dialog-button-role")
1613 {
1614 diag.push_error(
1615 format!("{prop} used outside of a Dialog's direct child"),
1616 &*expr.borrow(),
1617 );
1618 }
1619 if (layout_type.is_none()
1620 || !recognized_layout_types().contains(&layout_type.as_ref().unwrap().as_str()))
1621 && matches!(
1622 prop.as_ref(),
1623 "padding" | "padding-left" | "padding-right" | "padding-top" | "padding-bottom"
1624 )
1625 && !check_inherits_layout(item)
1626 {
1627 diag.push_warning(
1628 format!("{prop} only has effect on layout elements"),
1629 &*expr.borrow(),
1630 );
1631 }
1632 }
1633
1634 fn check_inherits_layout(item: &ElementRc) -> bool {
1636 if let ElementType::Component(c) = &item.borrow().base_type {
1637 c.root_element.borrow().debug.iter().any(|d| d.layout.is_some())
1638 || check_inherits_layout(&c.root_element)
1639 } else {
1640 false
1641 }
1642 }
1643}
1644
1645pub fn check_window_layout(component: &Rc<Component>) {
1651 if component.root_constraints.borrow().fixed_height {
1652 adjust_window_layout(component, "height");
1653 }
1654 if component.root_constraints.borrow().fixed_width {
1655 adjust_window_layout(component, "width");
1656 }
1657}
1658
1659fn adjust_window_layout(component: &Rc<Component>, prop: &'static str) {
1660 let new_prop = crate::layout::create_new_prop(
1661 &component.root_element,
1662 format_smolstr!("fixed-{prop}"),
1663 Type::LogicalLength,
1664 );
1665 {
1666 let mut root = component.root_element.borrow_mut();
1667 if let Some(b) = root.bindings.remove(prop) {
1668 root.bindings.insert(new_prop.name().clone(), b);
1669 };
1670 let mut analysis = root.property_analysis.borrow_mut();
1671 if let Some(a) = analysis.remove(prop) {
1672 analysis.insert(new_prop.name().clone(), a);
1673 };
1674 drop(analysis);
1675 root.bindings.insert(
1676 prop.into(),
1677 RefCell::new(Expression::PropertyReference(new_prop.clone()).into()),
1678 );
1679 }
1680
1681 let old_prop = NamedReference::new(&component.root_element, SmolStr::new_static(prop));
1682 crate::object_tree::visit_all_named_references(component, &mut |nr| {
1683 if nr == &old_prop {
1684 *nr = new_prop.clone()
1685 }
1686 });
1687}