1use crate::diagnostics::{BuildDiagnostics, DiagnosticLevel, Spanned};
7use crate::expression_tree::*;
8use crate::langtype::{ElementType, PropertyLookupResult, Type};
9use crate::object_tree::{Component, ElementRc};
10
11use smol_str::{SmolStr, ToSmolStr, format_smolstr};
12
13use std::cell::RefCell;
14use std::rc::{Rc, Weak};
15
16#[derive(Clone, Debug, Copy, Eq, PartialEq)]
17pub enum Orientation {
18 Horizontal,
19 Vertical,
20}
21
22impl Orientation {
23 pub fn orthogonal(self) -> Self {
24 match self {
25 Orientation::Horizontal => Orientation::Vertical,
26 Orientation::Vertical => Orientation::Horizontal,
27 }
28 }
29}
30
31#[derive(Clone, Debug, Copy, Eq, PartialEq, Default)]
32pub enum FlexboxLayoutDirection {
33 #[default]
35 Row,
36 RowReverse,
38 Column,
40 ColumnReverse,
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46pub enum FlexboxAxisRelation {
47 MainAxis,
49 CrossAxis,
51 Unknown,
53}
54
55#[derive(Clone, Debug, derive_more::From)]
56pub enum Layout {
57 GridLayout(GridLayout),
58 BoxLayout(BoxLayout),
59 FlexboxLayout(FlexboxLayout),
60}
61
62impl Layout {
63 pub fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
65 match self {
66 Layout::GridLayout(grid) => grid.visit_named_references(visitor),
67 Layout::BoxLayout(l) => l.visit_named_references(visitor),
68 Layout::FlexboxLayout(l) => l.visit_named_references(visitor),
69 }
70 }
71}
72
73#[derive(Debug, Default, Clone)]
75pub struct LayoutItem {
76 pub element: ElementRc,
77 pub constraints: LayoutConstraints,
78}
79
80#[derive(Debug, Clone)]
82pub struct FlexboxLayoutItem {
83 pub item: LayoutItem,
84 pub flex_grow: Option<NamedReference>,
85 pub flex_shrink: Option<NamedReference>,
86 pub flex_basis: Option<NamedReference>,
87 pub align_self: Option<NamedReference>,
88 pub order: Option<NamedReference>,
89}
90
91#[derive(Debug, Clone)]
94pub enum RowChildTemplate {
95 Static(LayoutItem),
96 Repeated {
97 item: LayoutItem,
98 repeated_element: ElementRc,
100 },
101}
102
103impl RowChildTemplate {
104 pub fn layout_item(&self) -> &LayoutItem {
105 match self {
106 RowChildTemplate::Static(item) => item,
107 RowChildTemplate::Repeated { item, .. } => item,
108 }
109 }
110
111 pub fn layout_item_mut(&mut self) -> &mut LayoutItem {
112 match self {
113 RowChildTemplate::Static(item) => item,
114 RowChildTemplate::Repeated { item, .. } => item,
115 }
116 }
117
118 pub fn repeated_element(&self) -> Option<&ElementRc> {
119 match self {
120 RowChildTemplate::Static(_) => None,
121 RowChildTemplate::Repeated { repeated_element, .. } => Some(repeated_element),
122 }
123 }
124
125 pub fn is_repeated(&self) -> bool {
126 self.repeated_element().is_some()
127 }
128}
129
130impl LayoutItem {
131 pub fn rect(&self) -> LayoutRect {
132 let p = |unresolved_name: &str| {
133 let PropertyLookupResult { resolved_name, property_type, .. } =
134 self.element.borrow().lookup_property(unresolved_name);
135 if property_type == Type::LogicalLength {
136 Some(NamedReference::new(&self.element, resolved_name.to_smolstr()))
137 } else {
138 None
139 }
140 };
141 LayoutRect {
142 x_reference: p("x"),
143 y_reference: p("y"),
144 width_reference: if !self.constraints.fixed_width { p("width") } else { None },
145 height_reference: if !self.constraints.fixed_height { p("height") } else { None },
146 }
147 }
148}
149
150#[derive(Debug, Clone, Default)]
151pub struct LayoutRect {
152 pub width_reference: Option<NamedReference>,
153 pub height_reference: Option<NamedReference>,
154 pub x_reference: Option<NamedReference>,
155 pub y_reference: Option<NamedReference>,
156}
157
158impl LayoutRect {
159 pub fn install_on_element(element: &ElementRc) -> Self {
160 let install_prop =
161 |name: &'static str| Some(NamedReference::new(element, SmolStr::new_static(name)));
162
163 Self {
164 x_reference: install_prop("x"),
165 y_reference: install_prop("y"),
166 width_reference: install_prop("width"),
167 height_reference: install_prop("height"),
168 }
169 }
170
171 fn visit_named_references(&mut self, mut visitor: &mut impl FnMut(&mut NamedReference)) {
172 self.width_reference.as_mut().map(&mut visitor);
173 self.height_reference.as_mut().map(&mut visitor);
174 self.x_reference.as_mut().map(&mut visitor);
175 self.y_reference.as_mut().map(&mut visitor);
176 }
177
178 pub fn size_reference(&self, orientation: Orientation) -> Option<&NamedReference> {
179 match orientation {
180 Orientation::Horizontal => self.width_reference.as_ref(),
181 Orientation::Vertical => self.height_reference.as_ref(),
182 }
183 }
184}
185
186#[derive(Debug, Default, Clone)]
187pub struct LayoutConstraints {
188 pub min_width: Option<NamedReference>,
189 pub max_width: Option<NamedReference>,
190 pub min_height: Option<NamedReference>,
191 pub max_height: Option<NamedReference>,
192 pub preferred_width: Option<NamedReference>,
193 pub preferred_height: Option<NamedReference>,
194 pub horizontal_stretch: Option<NamedReference>,
195 pub vertical_stretch: Option<NamedReference>,
196 pub fixed_width: bool,
197 pub fixed_height: bool,
198}
199
200impl LayoutConstraints {
201 pub fn new(
207 element: &ElementRc,
208 mut diag: Option<(&mut BuildDiagnostics, DiagnosticLevel)>,
209 ) -> Self {
210 let mut constraints = Self {
211 min_width: binding_reference(element, "min-width"),
212 max_width: binding_reference(element, "max-width"),
213 min_height: binding_reference(element, "min-height"),
214 max_height: binding_reference(element, "max-height"),
215 preferred_width: binding_reference(element, "preferred-width"),
216 preferred_height: binding_reference(element, "preferred-height"),
217 horizontal_stretch: binding_reference(element, "horizontal-stretch"),
218 vertical_stretch: binding_reference(element, "vertical-stretch"),
219 fixed_width: false,
220 fixed_height: false,
221 };
222 let mut apply_size_constraint =
223 |prop: &'static str,
224 binding: &BindingExpression,
225 enclosing1: &Weak<Component>,
226 depth,
227 op: &mut Option<NamedReference>| {
228 if let Some(other_prop) = op {
229 find_binding(
230 &other_prop.element(),
231 other_prop.name(),
232 |old, enclosing2, d2| {
233 if let Some((diag, level)) = &mut diag
234 && Weak::ptr_eq(enclosing1, enclosing2)
235 && old.priority.saturating_add(d2)
236 <= binding.priority.saturating_add(depth)
237 {
238 diag.push_diagnostic_with_span(
239 format!(
240 "Cannot specify both '{prop}' and '{}'",
241 other_prop.name()
242 ),
243 binding.to_source_location(),
244 *level,
245 );
246 }
247 },
248 );
249 }
250 *op = Some(NamedReference::new(element, SmolStr::new_static(prop)))
251 };
252 find_binding(element, "height", |s, enclosing, depth| {
253 constraints.fixed_height = true;
254 apply_size_constraint("height", s, enclosing, depth, &mut constraints.min_height);
255 apply_size_constraint("height", s, enclosing, depth, &mut constraints.max_height);
256 });
257 find_binding(element, "width", |s, enclosing, depth| {
258 constraints.fixed_width = true;
259 if s.expression.ty() == Type::Percent {
260 apply_size_constraint("width", s, enclosing, depth, &mut constraints.min_width);
261 } else {
262 apply_size_constraint("width", s, enclosing, depth, &mut constraints.min_width);
263 apply_size_constraint("width", s, enclosing, depth, &mut constraints.max_width);
264 }
265 });
266
267 constraints
268 }
269
270 pub fn has_explicit_restrictions(&self, orientation: Orientation) -> bool {
271 match orientation {
272 Orientation::Horizontal => {
273 self.min_width.is_some()
274 || self.max_width.is_some()
275 || self.preferred_width.is_some()
276 || self.horizontal_stretch.is_some()
277 }
278 Orientation::Vertical => {
279 self.min_height.is_some()
280 || self.max_height.is_some()
281 || self.preferred_height.is_some()
282 || self.vertical_stretch.is_some()
283 }
284 }
285 }
286
287 pub fn for_each_restrictions(
289 &self,
290 orientation: Orientation,
291 ) -> impl Iterator<Item = (&NamedReference, &'static str)> {
292 let (min, max, preferred, stretch) = match orientation {
293 Orientation::Horizontal => {
294 (&self.min_width, &self.max_width, &self.preferred_width, &self.horizontal_stretch)
295 }
296 Orientation::Vertical => {
297 (&self.min_height, &self.max_height, &self.preferred_height, &self.vertical_stretch)
298 }
299 };
300 std::iter::empty()
301 .chain(min.as_ref().map(|x| {
302 if Expression::PropertyReference(x.clone()).ty() != Type::Percent {
303 (x, "min")
304 } else {
305 (x, "min_percent")
306 }
307 }))
308 .chain(max.as_ref().map(|x| {
309 if Expression::PropertyReference(x.clone()).ty() != Type::Percent {
310 (x, "max")
311 } else {
312 (x, "max_percent")
313 }
314 }))
315 .chain(preferred.as_ref().map(|x| (x, "preferred")))
316 .chain(stretch.as_ref().map(|x| (x, "stretch")))
317 }
318
319 pub fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
320 if let Some(e) = self.max_width.as_mut() {
321 visitor(&mut *e);
322 }
323 if let Some(e) = self.min_width.as_mut() {
324 visitor(&mut *e);
325 }
326 if let Some(e) = self.max_height.as_mut() {
327 visitor(&mut *e);
328 }
329 if let Some(e) = self.min_height.as_mut() {
330 visitor(&mut *e);
331 }
332 if let Some(e) = self.preferred_width.as_mut() {
333 visitor(&mut *e);
334 }
335 if let Some(e) = self.preferred_height.as_mut() {
336 visitor(&mut *e);
337 }
338 if let Some(e) = self.horizontal_stretch.as_mut() {
339 visitor(&mut *e);
340 }
341 if let Some(e) = self.vertical_stretch.as_mut() {
342 visitor(&mut *e);
343 }
344 }
345}
346
347#[derive(Debug, Clone)]
348pub enum RowColExpr {
349 Named(NamedReference),
350 Literal(u16),
351 Auto,
352}
353
354#[derive(Debug, Clone)]
355pub struct GridLayoutCell {
356 pub new_row: bool,
357 pub col_expr: RowColExpr,
358 pub row_expr: RowColExpr,
359 pub colspan_expr: RowColExpr,
360 pub rowspan_expr: RowColExpr,
361 pub child_items: Option<Vec<RowChildTemplate>>, }
363
364impl GridLayoutCell {
365 pub fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
366 if let RowColExpr::Named(ref mut e) = self.col_expr {
367 visitor(e);
368 }
369 if let RowColExpr::Named(ref mut e) = self.row_expr {
370 visitor(e);
371 }
372 if let RowColExpr::Named(ref mut e) = self.colspan_expr {
373 visitor(e);
374 }
375 if let RowColExpr::Named(ref mut e) = self.rowspan_expr {
376 visitor(e);
377 }
378 if let Some(children) = &mut self.child_items {
379 for child in children {
380 child.layout_item_mut().constraints.visit_named_references(visitor);
381 }
382 }
383 }
384}
385
386#[derive(Debug, Clone)]
388pub struct GridLayoutElement {
389 pub cell: Rc<RefCell<GridLayoutCell>>,
391 pub item: LayoutItem,
392}
393
394impl GridLayoutElement {
395 pub fn span(&self, orientation: Orientation) -> RowColExpr {
396 let cell = self.cell.borrow();
397 match orientation {
398 Orientation::Horizontal => cell.colspan_expr.clone(),
399 Orientation::Vertical => cell.rowspan_expr.clone(),
400 }
401 }
402}
403
404#[derive(Debug, Clone)]
405pub struct Padding {
406 pub left: Option<NamedReference>,
407 pub right: Option<NamedReference>,
408 pub top: Option<NamedReference>,
409 pub bottom: Option<NamedReference>,
410}
411
412impl Padding {
413 fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
414 if let Some(e) = self.left.as_mut() {
415 visitor(&mut *e)
416 }
417 if let Some(e) = self.right.as_mut() {
418 visitor(&mut *e)
419 }
420 if let Some(e) = self.top.as_mut() {
421 visitor(&mut *e)
422 }
423 if let Some(e) = self.bottom.as_mut() {
424 visitor(&mut *e)
425 }
426 }
427
428 pub fn begin_end(&self, o: Orientation) -> (Option<&NamedReference>, Option<&NamedReference>) {
430 match o {
431 Orientation::Horizontal => (self.left.as_ref(), self.right.as_ref()),
432 Orientation::Vertical => (self.top.as_ref(), self.bottom.as_ref()),
433 }
434 }
435}
436
437#[derive(Debug, Clone)]
438pub struct Spacing {
439 pub horizontal: Option<NamedReference>,
440 pub vertical: Option<NamedReference>,
441}
442
443impl Spacing {
444 fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
445 if let Some(e) = self.horizontal.as_mut() {
446 visitor(&mut *e);
447 }
448 if let Some(e) = self.vertical.as_mut() {
449 visitor(&mut *e);
450 }
451 }
452
453 pub fn orientation(&self, o: Orientation) -> Option<&NamedReference> {
454 match o {
455 Orientation::Horizontal => self.horizontal.as_ref(),
456 Orientation::Vertical => self.vertical.as_ref(),
457 }
458 }
459}
460
461#[derive(Debug, Clone)]
462pub struct LayoutGeometry {
463 pub rect: LayoutRect,
464 pub spacing: Spacing,
465 pub alignment: Option<NamedReference>,
466 pub padding: Padding,
467}
468
469impl LayoutGeometry {
470 pub fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
471 self.rect.visit_named_references(visitor);
472 if let Some(e) = self.alignment.as_mut() {
473 visitor(&mut *e)
474 }
475 self.spacing.visit_named_references(visitor);
476 self.padding.visit_named_references(visitor);
477 }
478
479 pub fn new(layout_element: &ElementRc) -> Self {
480 let spacing = || binding_reference(layout_element, "spacing");
481 init_fake_property(layout_element, "spacing-horizontal", spacing);
482 init_fake_property(layout_element, "spacing-vertical", spacing);
483
484 let alignment = binding_reference(layout_element, "alignment");
485
486 let padding = || binding_reference(layout_element, "padding");
487 init_fake_property(layout_element, "padding-left", padding);
488 init_fake_property(layout_element, "padding-right", padding);
489 init_fake_property(layout_element, "padding-top", padding);
490 init_fake_property(layout_element, "padding-bottom", padding);
491
492 let padding = Padding {
493 left: binding_reference(layout_element, "padding-left").or_else(padding),
494 right: binding_reference(layout_element, "padding-right").or_else(padding),
495 top: binding_reference(layout_element, "padding-top").or_else(padding),
496 bottom: binding_reference(layout_element, "padding-bottom").or_else(padding),
497 };
498
499 let spacing = Spacing {
500 horizontal: binding_reference(layout_element, "spacing-horizontal").or_else(spacing),
501 vertical: binding_reference(layout_element, "spacing-vertical").or_else(spacing),
502 };
503
504 let rect = LayoutRect::install_on_element(layout_element);
505
506 Self { rect, spacing, padding, alignment }
507 }
508}
509
510fn find_binding<R>(
513 element: &ElementRc,
514 name: &str,
515 f: impl FnOnce(&BindingExpression, &Weak<Component>, i32) -> R,
516) -> Option<R> {
517 let mut element = element.clone();
518 let mut depth = 0;
519 loop {
520 if let Some(b) = element.borrow().bindings.get(name)
521 && b.borrow().has_binding()
522 {
523 return Some(f(&b.borrow(), &element.borrow().enclosing_component, depth));
524 }
525 let e = match &element.borrow().base_type {
526 ElementType::Component(base) => base.root_element.clone(),
527 _ => return None,
528 };
529 element = e;
530 depth += 1;
531 }
532}
533
534pub fn binding_reference(element: &ElementRc, name: &'static str) -> Option<NamedReference> {
536 find_binding(element, name, |_, _, _| NamedReference::new(element, SmolStr::new_static(name)))
537}
538
539fn init_fake_property(
540 grid_layout_element: &ElementRc,
541 name: &str,
542 lazy_default: impl Fn() -> Option<NamedReference>,
543) {
544 if grid_layout_element.borrow().property_declarations.contains_key(name)
545 && !grid_layout_element.borrow().bindings.contains_key(name)
546 && let Some(e) = lazy_default()
547 {
548 if e.name() == name && Rc::ptr_eq(&e.element(), grid_layout_element) {
549 return;
551 }
552 grid_layout_element
553 .borrow_mut()
554 .bindings
555 .insert(name.into(), RefCell::new(Expression::PropertyReference(e).into()));
556 }
557}
558
559#[derive(Debug, Clone)]
561pub struct GridLayout {
562 pub elems: Vec<GridLayoutElement>,
564
565 pub geometry: LayoutGeometry,
566
567 pub dialog_button_roles: Option<Vec<SmolStr>>,
570
571 pub uses_auto: bool,
573}
574
575impl GridLayout {
576 pub fn clone_cells(&mut self) {
578 for e in &mut self.elems {
579 let cloned = Rc::new(RefCell::new(e.cell.borrow().clone()));
580 e.cell = cloned;
581 }
582 }
583
584 pub fn visit_rowcol_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
585 for elem in &mut self.elems {
586 let mut cell = elem.cell.borrow_mut();
587 if let RowColExpr::Named(ref mut e) = cell.col_expr {
588 visitor(e);
589 }
590 if let RowColExpr::Named(ref mut e) = cell.row_expr {
591 visitor(e);
592 }
593 if let RowColExpr::Named(ref mut e) = cell.colspan_expr {
594 visitor(e);
595 }
596 if let RowColExpr::Named(ref mut e) = cell.rowspan_expr {
597 visitor(e);
598 }
599 }
600 }
601
602 pub fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
603 self.visit_rowcol_named_references(visitor);
604 for layout_elem in &mut self.elems {
605 layout_elem.item.constraints.visit_named_references(visitor);
606 if let Some(child_items) = &mut layout_elem.cell.borrow_mut().child_items {
607 for child in child_items {
608 child.layout_item_mut().constraints.visit_named_references(visitor);
609 }
610 }
611 }
612 self.geometry.visit_named_references(visitor);
613 }
614}
615
616#[derive(Debug, Clone)]
618pub struct BoxLayout {
619 pub orientation: Orientation,
621 pub elems: Vec<LayoutItem>,
622 pub geometry: LayoutGeometry,
623 pub cross_alignment: Option<NamedReference>,
625}
626
627impl BoxLayout {
628 pub fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
629 for cell in &mut self.elems {
630 cell.constraints.visit_named_references(visitor);
631 }
632 self.geometry.visit_named_references(visitor);
633 if let Some(e) = self.cross_alignment.as_mut() {
634 visitor(&mut *e);
635 }
636 }
637}
638
639#[derive(Debug, Clone)]
641pub struct FlexboxLayout {
642 pub elems: Vec<FlexboxLayoutItem>,
643 pub geometry: LayoutGeometry,
644 pub direction: Option<NamedReference>,
645 pub align_content: Option<NamedReference>,
646 pub cross_axis_alignment: Option<NamedReference>,
647 pub flex_wrap: Option<NamedReference>,
648}
649
650impl FlexboxLayout {
651 fn compile_time_direction(&self) -> Option<FlexboxLayoutDirection> {
654 match self.direction.as_ref() {
655 None => Some(FlexboxLayoutDirection::Row),
656 Some(nr) => nr.element().borrow().bindings.get(nr.name()).and_then(|binding| {
657 if let crate::expression_tree::Expression::EnumerationValue(ev) =
658 &binding.borrow().expression
659 {
660 match ev.enumeration.values[ev.value].as_str() {
661 "row" => Some(FlexboxLayoutDirection::Row),
662 "row-reverse" => Some(FlexboxLayoutDirection::RowReverse),
663 "column" => Some(FlexboxLayoutDirection::Column),
664 "column-reverse" => Some(FlexboxLayoutDirection::ColumnReverse),
665 _ => None,
666 }
667 } else {
668 None
669 }
670 }),
671 }
672 }
673
674 pub fn axis_relation(&self, orientation: Orientation) -> FlexboxAxisRelation {
676 match self.compile_time_direction() {
677 None => FlexboxAxisRelation::Unknown,
678 Some(dir) => {
679 let is_main = matches!(
680 (dir, orientation),
681 (
682 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse,
683 Orientation::Horizontal
684 ) | (
685 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse,
686 Orientation::Vertical
687 )
688 );
689 if is_main { FlexboxAxisRelation::MainAxis } else { FlexboxAxisRelation::CrossAxis }
690 }
691 }
692 }
693
694 pub fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) {
695 for cell in &mut self.elems {
696 cell.item.constraints.visit_named_references(visitor);
697 if let Some(e) = cell.flex_grow.as_mut() {
698 visitor(&mut *e)
699 }
700 if let Some(e) = cell.flex_shrink.as_mut() {
701 visitor(&mut *e)
702 }
703 if let Some(e) = cell.flex_basis.as_mut() {
704 visitor(&mut *e)
705 }
706 if let Some(e) = cell.align_self.as_mut() {
707 visitor(&mut *e)
708 }
709 if let Some(e) = cell.order.as_mut() {
710 visitor(&mut *e)
711 }
712 }
713 self.geometry.visit_named_references(visitor);
714 if let Some(e) = self.direction.as_mut() {
715 visitor(&mut *e)
716 }
717 if let Some(e) = self.align_content.as_mut() {
718 visitor(&mut *e)
719 }
720 if let Some(e) = self.cross_axis_alignment.as_mut() {
721 visitor(&mut *e)
722 }
723 if let Some(e) = self.flex_wrap.as_mut() {
724 visitor(&mut *e)
725 }
726 }
727}
728
729#[derive(Clone, Copy, PartialEq)]
732pub enum BuiltinFilter {
733 All,
735 SkipNonImplicit,
737}
738
739pub fn implicit_layout_info_call(
743 elem: &ElementRc,
744 orientation: Orientation,
745 filter: BuiltinFilter,
746 constraint: Option<Expression>,
747) -> Option<Expression> {
748 let mut elem_it = elem.clone();
749 loop {
750 return match &elem_it.clone().borrow().base_type {
751 ElementType::Component(base_comp) => {
752 let parametrized_nr = constraint.as_ref().and_then(|_| match orientation {
756 Orientation::Vertical => {
757 base_comp.root_element.borrow().layout_info_v_with_constraint.clone()
758 }
759 Orientation::Horizontal => {
760 base_comp.root_element.borrow().layout_info_h_with_constraint.clone()
761 }
762 });
763 if let Some(nr) = parametrized_nr
764 && let Some(c) = &constraint
765 {
766 debug_assert!(Rc::ptr_eq(&nr.element(), &base_comp.root_element));
767 return Some(Expression::FunctionCall {
768 function: crate::expression_tree::Callable::Function(NamedReference::new(
769 elem,
770 nr.name().clone(),
771 )),
772 arguments: vec![c.clone()],
773 source_location: None,
774 });
775 }
776 match base_comp.root_element.borrow().layout_info_prop(orientation) {
777 Some(nr) => {
778 debug_assert!(Rc::ptr_eq(&nr.element(), &base_comp.root_element));
781 Some(Expression::PropertyReference(NamedReference::new(
782 elem,
783 nr.name().clone(),
784 )))
785 }
786 None => {
787 elem_it = base_comp.root_element.clone();
788 continue;
789 }
790 }
791 }
792 ElementType::Builtin(base_type)
793 if matches!(
794 base_type.name.as_str(),
795 "Rectangle"
796 | "Empty"
797 | "TouchArea"
798 | "FocusScope"
799 | "Opacity"
800 | "Layer"
801 | "BoxShadow"
802 | "Clip"
803 ) =>
804 {
805 if filter == BuiltinFilter::SkipNonImplicit {
806 return None;
807 }
808 Some(Expression::Struct {
811 ty: crate::typeregister::layout_info_type(),
812 values: [("min", 0.), ("max", f32::MAX), ("preferred", 0.)]
813 .iter()
814 .map(|(s, v)| {
815 (SmolStr::new_static(s), Expression::NumberLiteral(*v as _, Unit::Px))
816 })
817 .chain(
818 [("min_percent", 0.), ("max_percent", 100.), ("stretch", 1.)]
819 .iter()
820 .map(|(s, v)| {
821 (
822 SmolStr::new_static(s),
823 Expression::NumberLiteral(*v, Unit::None),
824 )
825 }),
826 )
827 .collect(),
828 })
829 }
830 ElementType::Builtin(base_type)
831 if filter == BuiltinFilter::SkipNonImplicit
832 && base_type.default_size_binding
833 != crate::langtype::DefaultSizeBinding::ImplicitSize =>
834 {
835 None
836 }
837 _ => Some(Expression::FunctionCall {
838 function: BuiltinFunction::ImplicitLayoutInfo(orientation).into(),
839 arguments: vec![
840 Expression::ElementReference(Rc::downgrade(elem)),
841 constraint.unwrap_or(Expression::NumberLiteral(-1., Unit::None)),
842 ],
843 source_location: None,
844 }),
845 };
846 }
847}
848
849pub fn create_new_prop(elem: &ElementRc, tentative_name: SmolStr, ty: Type) -> NamedReference {
851 let mut e = elem.borrow_mut();
852 if !e.lookup_property(&tentative_name).is_valid() {
853 e.property_declarations.insert(tentative_name.clone(), ty.into());
854 drop(e);
855 NamedReference::new(elem, tentative_name)
856 } else {
857 let mut counter = 0;
858 loop {
859 counter += 1;
860 let name = format_smolstr!("{}{}", tentative_name, counter);
861 if !e.lookup_property(&name).is_valid() {
862 e.property_declarations.insert(name.clone(), ty.into());
863 drop(e);
864 return NamedReference::new(elem, name);
865 }
866 }
867 }
868}
869
870pub fn is_layout(base_type: &ElementType) -> bool {
872 match base_type {
873 ElementType::Component(c) => is_layout(&c.root_element.borrow().base_type),
874 ElementType::Builtin(be) => {
875 matches!(
876 be.name.as_str(),
877 "GridLayout" | "HorizontalLayout" | "VerticalLayout" | "FlexboxLayout"
878 )
879 }
880 _ => false,
881 }
882}