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