1use crate::diagnostics::{BuildDiagnostics, DiagnosticLevel, Spanned};
7use crate::expression_tree::*;
8use crate::langtype::{ElementType, PropertyLookupMode, PropertyLookupResult, Type};
9use crate::object_tree::{Component, ElementRc};
10
11use smol_str::{SmolStr, ToSmolStr};
12
13use std::cell::RefCell;
14use std::rc::{Rc, Weak};
15
16pub const BOX_LAYOUT_CACHE_ENTRIES_PER_CELL: usize = 2;
18
19#[derive(Clone, Debug, Copy, Eq, PartialEq)]
20pub enum Orientation {
21 Horizontal,
22 Vertical,
23}
24
25impl Orientation {
26 pub fn orthogonal(self) -> Self {
27 match self {
28 Orientation::Horizontal => Orientation::Vertical,
29 Orientation::Vertical => Orientation::Horizontal,
30 }
31 }
32}
33
34#[derive(Clone, Debug, Copy, Eq, PartialEq, Default)]
35pub enum FlexboxLayoutDirection {
36 #[default]
38 Row,
39 RowReverse,
41 Column,
43 ColumnReverse,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
49pub enum FlexboxAxisRelation {
50 MainAxis,
52 CrossAxis,
54 Unknown,
56}
57
58#[derive(Clone, Debug, derive_more::From)]
59pub enum Layout {
60 GridLayout(GridLayout),
61 BoxLayout(BoxLayout),
62 FlexboxLayout(FlexboxLayout),
63}
64
65impl Layout {
66 pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
68 match self {
69 Layout::GridLayout(grid) => grid.visit_named_references(visitor),
70 Layout::BoxLayout(l) => l.visit_named_references(visitor),
71 Layout::FlexboxLayout(l) => l.visit_named_references(visitor),
72 }
73 }
74}
75
76#[derive(Debug, Default, Clone)]
78pub struct LayoutItem {
79 pub element: ElementRc,
80 pub constraints: LayoutConstraints,
81 pub cross_axis_self_alignment: Option<NamedReference>,
84 pub layout_order: Option<NamedReference>,
87}
88
89#[derive(Debug, Clone)]
92pub enum RowChildTemplate {
93 Static(LayoutItem),
94 Repeated {
95 item: LayoutItem,
96 repeated_element: ElementRc,
98 },
99}
100
101impl RowChildTemplate {
102 pub fn layout_item(&self) -> &LayoutItem {
103 match self {
104 RowChildTemplate::Static(item) => item,
105 RowChildTemplate::Repeated { item, .. } => item,
106 }
107 }
108
109 pub fn layout_item_mut(&mut self) -> &mut LayoutItem {
110 match self {
111 RowChildTemplate::Static(item) => item,
112 RowChildTemplate::Repeated { item, .. } => item,
113 }
114 }
115
116 pub fn repeated_element(&self) -> Option<&ElementRc> {
117 match self {
118 RowChildTemplate::Static(_) => None,
119 RowChildTemplate::Repeated { repeated_element, .. } => Some(repeated_element),
120 }
121 }
122
123 pub fn is_repeated(&self) -> bool {
124 self.repeated_element().is_some()
125 }
126}
127
128impl LayoutItem {
129 pub fn rect(&self) -> LayoutRect {
130 let p = |unresolved_name: &str| {
131 let PropertyLookupResult { resolved_name, property_type, .. } = self
132 .element
133 .borrow()
134 .lookup_property(unresolved_name, PropertyLookupMode::ComponentLocal);
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 dyn 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 pub local: LayoutConstraintLocality,
205}
206
207#[derive(Debug, Default, Clone)]
210pub struct LayoutConstraintLocality {
211 pub min_width: bool,
212 pub max_width: bool,
213 pub min_height: bool,
214 pub max_height: bool,
215 pub preferred_width: bool,
216 pub preferred_height: bool,
217 pub horizontal_stretch: bool,
218 pub vertical_stretch: bool,
219}
220
221pub struct OrientationConstraints<'a> {
223 pub min: &'a Option<NamedReference>,
224 pub max: &'a Option<NamedReference>,
225 pub preferred: &'a Option<NamedReference>,
226 pub stretch: &'a Option<NamedReference>,
227 pub fixed: bool,
229}
230
231impl LayoutConstraints {
232 pub fn new(
238 element: &ElementRc,
239 mut diag: Option<(&mut BuildDiagnostics, DiagnosticLevel)>,
240 ) -> Self {
241 let mut constraints = Self {
242 min_width: binding_reference(element, "min-width"),
243 max_width: binding_reference(element, "max-width"),
244 min_height: binding_reference(element, "min-height"),
245 max_height: binding_reference(element, "max-height"),
246 preferred_width: binding_reference(element, "preferred-width"),
247 preferred_height: binding_reference(element, "preferred-height"),
248 horizontal_stretch: binding_reference(element, "horizontal-stretch"),
249 vertical_stretch: binding_reference(element, "vertical-stretch"),
250 fixed_width: false,
251 fixed_height: false,
252 local: LayoutConstraintLocality {
253 min_width: is_local_binding(element, "min-width")
257 || is_local_binding(element, "width"),
258 max_width: is_local_binding(element, "max-width")
259 || is_local_binding(element, "width"),
260 min_height: is_local_binding(element, "min-height")
261 || is_local_binding(element, "height"),
262 max_height: is_local_binding(element, "max-height")
263 || is_local_binding(element, "height"),
264 preferred_width: is_local_binding(element, "preferred-width"),
265 preferred_height: is_local_binding(element, "preferred-height"),
266 horizontal_stretch: is_local_binding(element, "horizontal-stretch"),
267 vertical_stretch: is_local_binding(element, "vertical-stretch"),
268 },
269 };
270 let mut apply_size_constraint =
271 |prop: &'static str,
272 binding: &BindingExpression,
273 enclosing1: &Weak<Component>,
274 depth,
275 op: &mut Option<NamedReference>| {
276 if let Some(other_prop) = op {
277 find_binding(
278 &other_prop.element(),
279 other_prop.name(),
280 |old, enclosing2, d2| {
281 if let Some((diag, level)) = &mut diag
282 && Weak::ptr_eq(enclosing1, enclosing2)
283 && old.priority.saturating_add(d2)
284 <= binding.priority.saturating_add(depth)
285 {
286 diag.push_diagnostic_with_span(
287 format!(
288 "Cannot specify both '{prop}' and '{}'",
289 other_prop.name()
290 ),
291 binding.to_source_location(),
292 *level,
293 );
294 }
295 },
296 );
297 }
298 *op = Some(NamedReference::new(element, SmolStr::new_static(prop)))
299 };
300 find_binding(element, "height", |s, enclosing, depth| {
301 constraints.fixed_height = true;
302 apply_size_constraint("height", s, enclosing, depth, &mut constraints.min_height);
303 apply_size_constraint("height", s, enclosing, depth, &mut constraints.max_height);
304 });
305 find_binding(element, "width", |s, enclosing, depth| {
306 constraints.fixed_width = true;
307 if s.expression.ty() == Type::Percent {
308 apply_size_constraint("width", s, enclosing, depth, &mut constraints.min_width);
309 } else {
310 apply_size_constraint("width", s, enclosing, depth, &mut constraints.min_width);
311 apply_size_constraint("width", s, enclosing, depth, &mut constraints.max_width);
312 }
313 });
314
315 constraints
316 }
317
318 pub fn has_explicit_restrictions(&self, orientation: Orientation) -> bool {
319 match orientation {
320 Orientation::Horizontal => {
321 self.min_width.is_some()
322 || self.max_width.is_some()
323 || self.preferred_width.is_some()
324 || self.horizontal_stretch.is_some()
325 }
326 Orientation::Vertical => {
327 self.min_height.is_some()
328 || self.max_height.is_some()
329 || self.preferred_height.is_some()
330 || self.vertical_stretch.is_some()
331 }
332 }
333 }
334
335 pub fn to_apply(&self, element: &ElementRc, orientation: Orientation) -> Self {
342 if !element.borrow().layout_info_includes_own_constraints(orientation) {
343 return self.clone();
344 }
345 let mut c = self.clone();
346 match orientation {
347 Orientation::Horizontal => {
348 if !self.local.min_width {
349 c.min_width = None;
350 }
351 if !self.local.max_width {
352 c.max_width = None;
353 }
354 if !self.local.preferred_width {
355 c.preferred_width = None;
356 }
357 if !self.local.horizontal_stretch {
358 c.horizontal_stretch = None;
359 }
360 }
361 Orientation::Vertical => {
362 if !self.local.min_height {
363 c.min_height = None;
364 }
365 if !self.local.max_height {
366 c.max_height = None;
367 }
368 if !self.local.preferred_height {
369 c.preferred_height = None;
370 }
371 if !self.local.vertical_stretch {
372 c.vertical_stretch = None;
373 }
374 }
375 }
376 c
377 }
378
379 pub fn for_orientation(&self, orientation: Orientation) -> OrientationConstraints<'_> {
380 match orientation {
381 Orientation::Horizontal => OrientationConstraints {
382 min: &self.min_width,
383 max: &self.max_width,
384 preferred: &self.preferred_width,
385 stretch: &self.horizontal_stretch,
386 fixed: self.fixed_width,
387 },
388 Orientation::Vertical => OrientationConstraints {
389 min: &self.min_height,
390 max: &self.max_height,
391 preferred: &self.preferred_height,
392 stretch: &self.vertical_stretch,
393 fixed: self.fixed_height,
394 },
395 }
396 }
397
398 pub fn for_each_restrictions(
400 &self,
401 orientation: Orientation,
402 ) -> impl Iterator<Item = (&NamedReference, &'static str)> {
403 let c = self.for_orientation(orientation);
404 std::iter::empty()
405 .chain(c.min.as_ref().map(|x| {
406 if Expression::PropertyReference(x.clone()).ty() != Type::Percent {
407 (x, "min")
408 } else {
409 (x, "min_percent")
410 }
411 }))
412 .chain(c.max.as_ref().map(|x| {
413 if Expression::PropertyReference(x.clone()).ty() != Type::Percent {
414 (x, "max")
415 } else {
416 (x, "max_percent")
417 }
418 }))
419 .chain(c.preferred.as_ref().map(|x| (x, "preferred")))
420 .chain(c.stretch.as_ref().map(|x| (x, "stretch")))
421 }
422
423 pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
424 if let Some(e) = self.max_width.as_mut() {
425 visitor(&mut *e);
426 }
427 if let Some(e) = self.min_width.as_mut() {
428 visitor(&mut *e);
429 }
430 if let Some(e) = self.max_height.as_mut() {
431 visitor(&mut *e);
432 }
433 if let Some(e) = self.min_height.as_mut() {
434 visitor(&mut *e);
435 }
436 if let Some(e) = self.preferred_width.as_mut() {
437 visitor(&mut *e);
438 }
439 if let Some(e) = self.preferred_height.as_mut() {
440 visitor(&mut *e);
441 }
442 if let Some(e) = self.horizontal_stretch.as_mut() {
443 visitor(&mut *e);
444 }
445 if let Some(e) = self.vertical_stretch.as_mut() {
446 visitor(&mut *e);
447 }
448 }
449}
450
451#[derive(Debug, Clone)]
452pub enum RowColExpr {
453 Named(NamedReference),
454 Literal(u16),
455 Auto,
456}
457
458#[derive(Debug, Clone)]
459pub struct GridLayoutCell {
460 pub new_row: bool,
461 pub col_expr: RowColExpr,
462 pub row_expr: RowColExpr,
463 pub colspan_expr: RowColExpr,
464 pub rowspan_expr: RowColExpr,
465 pub child_items: Option<Vec<RowChildTemplate>>, }
467
468impl GridLayoutCell {
469 pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
470 if let RowColExpr::Named(ref mut e) = self.col_expr {
471 visitor(e);
472 }
473 if let RowColExpr::Named(ref mut e) = self.row_expr {
474 visitor(e);
475 }
476 if let RowColExpr::Named(ref mut e) = self.colspan_expr {
477 visitor(e);
478 }
479 if let RowColExpr::Named(ref mut e) = self.rowspan_expr {
480 visitor(e);
481 }
482 if let Some(children) = &mut self.child_items {
483 for child in children {
484 child.layout_item_mut().constraints.visit_named_references(visitor);
485 }
486 }
487 }
488}
489
490#[derive(Debug, Clone)]
492pub struct GridLayoutElement {
493 pub cell: Rc<RefCell<GridLayoutCell>>,
495 pub item: LayoutItem,
496}
497
498impl GridLayoutElement {
499 pub fn span(&self, orientation: Orientation) -> RowColExpr {
500 let cell = self.cell.borrow();
501 match orientation {
502 Orientation::Horizontal => cell.colspan_expr.clone(),
503 Orientation::Vertical => cell.rowspan_expr.clone(),
504 }
505 }
506}
507
508#[derive(Debug, Clone)]
509pub struct Padding {
510 pub left: Option<NamedReference>,
511 pub right: Option<NamedReference>,
512 pub top: Option<NamedReference>,
513 pub bottom: Option<NamedReference>,
514}
515
516impl Padding {
517 fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
518 if let Some(e) = self.left.as_mut() {
519 visitor(&mut *e)
520 }
521 if let Some(e) = self.right.as_mut() {
522 visitor(&mut *e)
523 }
524 if let Some(e) = self.top.as_mut() {
525 visitor(&mut *e)
526 }
527 if let Some(e) = self.bottom.as_mut() {
528 visitor(&mut *e)
529 }
530 }
531
532 pub fn begin_end(&self, o: Orientation) -> (Option<&NamedReference>, Option<&NamedReference>) {
534 match o {
535 Orientation::Horizontal => (self.left.as_ref(), self.right.as_ref()),
536 Orientation::Vertical => (self.top.as_ref(), self.bottom.as_ref()),
537 }
538 }
539}
540
541#[derive(Debug, Clone)]
542pub struct Spacing {
543 pub horizontal: Option<NamedReference>,
544 pub vertical: Option<NamedReference>,
545}
546
547impl Spacing {
548 fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
549 if let Some(e) = self.horizontal.as_mut() {
550 visitor(&mut *e);
551 }
552 if let Some(e) = self.vertical.as_mut() {
553 visitor(&mut *e);
554 }
555 }
556
557 pub fn orientation(&self, o: Orientation) -> Option<&NamedReference> {
558 match o {
559 Orientation::Horizontal => self.horizontal.as_ref(),
560 Orientation::Vertical => self.vertical.as_ref(),
561 }
562 }
563}
564
565#[derive(Debug, Clone)]
566pub struct LayoutGeometry {
567 pub rect: LayoutRect,
568 pub spacing: Spacing,
569 pub alignment: Option<NamedReference>,
570 pub padding: Padding,
571}
572
573impl LayoutGeometry {
574 pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
575 self.rect.visit_named_references(visitor);
576 if let Some(e) = self.alignment.as_mut() {
577 visitor(&mut *e)
578 }
579 self.spacing.visit_named_references(visitor);
580 self.padding.visit_named_references(visitor);
581 }
582
583 pub fn new(layout_element: &ElementRc) -> Self {
584 let spacing = || binding_reference(layout_element, "spacing");
585 init_fake_property(layout_element, "spacing-horizontal", spacing);
586 init_fake_property(layout_element, "spacing-vertical", spacing);
587
588 let alignment = binding_reference(layout_element, "alignment");
589
590 let padding = || binding_reference(layout_element, "padding");
591 init_fake_property(layout_element, "padding-left", padding);
592 init_fake_property(layout_element, "padding-right", padding);
593 init_fake_property(layout_element, "padding-top", padding);
594 init_fake_property(layout_element, "padding-bottom", padding);
595
596 let padding = Padding {
597 left: binding_reference(layout_element, "padding-left").or_else(padding),
598 right: binding_reference(layout_element, "padding-right").or_else(padding),
599 top: binding_reference(layout_element, "padding-top").or_else(padding),
600 bottom: binding_reference(layout_element, "padding-bottom").or_else(padding),
601 };
602
603 let spacing = Spacing {
604 horizontal: binding_reference(layout_element, "spacing-horizontal").or_else(spacing),
605 vertical: binding_reference(layout_element, "spacing-vertical").or_else(spacing),
606 };
607
608 let rect = LayoutRect::install_on_element(layout_element);
609
610 Self { rect, spacing, padding, alignment }
611 }
612}
613
614pub(crate) fn find_binding<R>(
617 element: &ElementRc,
618 name: &str,
619 f: impl FnOnce(&BindingExpression, &Weak<Component>, i32) -> R,
620) -> Option<R> {
621 let mut element = element.clone();
622 let mut depth = 0;
623 loop {
624 if let Some(b) = element.borrow().binding(name)
625 && b.has_binding()
626 {
627 return Some(f(&b, &element.borrow().enclosing_component, depth));
628 }
629 let e = match &element.borrow().base_type {
630 ElementType::Component(base) => base.root_element.clone(),
631 _ => return None,
632 };
633 element = e;
634 depth += 1;
635 }
636}
637
638pub fn binding_reference(element: &ElementRc, name: &'static str) -> Option<NamedReference> {
640 find_binding(element, name, |_, _, _| NamedReference::new(element, SmolStr::new_static(name)))
641}
642
643fn is_local_binding(element: &ElementRc, name: &str) -> bool {
647 find_binding(element, name, |_, _, depth| depth == 0) == Some(true)
648}
649
650fn init_fake_property(
651 grid_layout_element: &ElementRc,
652 name: &str,
653 lazy_default: impl Fn() -> Option<NamedReference>,
654) {
655 if grid_layout_element.borrow().property_declarations.contains_key(name)
656 && grid_layout_element.borrow().binding(name).is_none()
657 && let Some(e) = lazy_default()
658 {
659 if e.name() == name && Rc::ptr_eq(&e.element(), grid_layout_element) {
660 return;
662 }
663 grid_layout_element
664 .borrow_mut()
665 .set_binding(name.into(), Expression::PropertyReference(e).into());
666 }
667}
668
669#[derive(Debug, Clone)]
671pub struct GridLayout {
672 pub elems: Vec<GridLayoutElement>,
674
675 pub geometry: LayoutGeometry,
676
677 pub dialog_button_roles: Option<Vec<SmolStr>>,
680
681 pub uses_auto: bool,
683}
684
685impl GridLayout {
686 pub fn clone_cells(&mut self) {
688 for e in &mut self.elems {
689 let cloned = Rc::new(RefCell::new(e.cell.borrow().clone()));
690 e.cell = cloned;
691 }
692 }
693
694 pub fn visit_rowcol_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
695 for elem in &mut self.elems {
696 let mut cell = elem.cell.borrow_mut();
697 if let RowColExpr::Named(ref mut e) = cell.col_expr {
698 visitor(e);
699 }
700 if let RowColExpr::Named(ref mut e) = cell.row_expr {
701 visitor(e);
702 }
703 if let RowColExpr::Named(ref mut e) = cell.colspan_expr {
704 visitor(e);
705 }
706 if let RowColExpr::Named(ref mut e) = cell.rowspan_expr {
707 visitor(e);
708 }
709 }
710 }
711
712 pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
713 self.visit_rowcol_named_references(visitor);
714 for layout_elem in &mut self.elems {
715 layout_elem.item.constraints.visit_named_references(visitor);
716 if let Some(child_items) = &mut layout_elem.cell.borrow_mut().child_items {
717 for child in child_items {
718 child.layout_item_mut().constraints.visit_named_references(visitor);
719 }
720 }
721 }
722 self.geometry.visit_named_references(visitor);
723 }
724}
725
726#[derive(Debug, Clone)]
728pub struct BoxLayout {
729 pub orientation: Orientation,
731 pub elems: Vec<LayoutItem>,
732 pub geometry: LayoutGeometry,
733 pub cross_alignment: Option<NamedReference>,
735}
736
737impl BoxLayout {
738 pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
739 for cell in &mut self.elems {
740 cell.constraints.visit_named_references(visitor);
741 if let Some(e) = cell.cross_axis_self_alignment.as_mut() {
742 visitor(&mut *e);
743 }
744 if let Some(e) = cell.layout_order.as_mut() {
745 visitor(&mut *e);
746 }
747 }
748 self.geometry.visit_named_references(visitor);
749 if let Some(e) = self.cross_alignment.as_mut() {
750 visitor(&mut *e);
751 }
752 }
753}
754
755#[derive(Debug, Clone)]
757pub struct FlexboxLayout {
758 pub elems: Vec<LayoutItem>,
759 pub geometry: LayoutGeometry,
760 pub direction: Option<NamedReference>,
761 pub cross_axis_line_alignment: Option<NamedReference>,
762 pub cross_axis_alignment: Option<NamedReference>,
763 pub flex_wrap: Option<NamedReference>,
764}
765
766impl FlexboxLayout {
767 pub fn from_element(elem: &ElementRc) -> Option<FlexboxLayout> {
771 use crate::expression_tree::Expression;
772 let nr = {
776 let eb = elem.borrow();
777 eb.effective_layout_info_prop(Orientation::Vertical)
778 .or_else(|| eb.effective_layout_info_prop(Orientation::Horizontal))
779 .cloned()
780 }?;
781 let target = nr.element();
782 let target = target.borrow();
783 let binding = target.binding(nr.name())?;
784 match binding.value_expression() {
785 Expression::ComputeFlexboxLayoutInfo { layout, .. } => Some(layout.clone()),
786 _ => None,
787 }
788 }
789
790 fn compile_time_direction(&self) -> Option<FlexboxLayoutDirection> {
793 match self.direction.as_ref() {
794 None => Some(FlexboxLayoutDirection::Row),
795 Some(nr) => nr.element().borrow().binding(nr.name()).and_then(|binding| {
796 if let crate::expression_tree::Expression::EnumerationValue(ev) =
797 binding.value_expression()
798 {
799 match ev.enumeration.values[ev.value].as_str() {
800 "row" => Some(FlexboxLayoutDirection::Row),
801 "row-reverse" => Some(FlexboxLayoutDirection::RowReverse),
802 "column" => Some(FlexboxLayoutDirection::Column),
803 "column-reverse" => Some(FlexboxLayoutDirection::ColumnReverse),
804 _ => None,
805 }
806 } else {
807 None
808 }
809 }),
810 }
811 }
812
813 pub fn axis_relation(&self, orientation: Orientation) -> FlexboxAxisRelation {
815 match self.compile_time_direction() {
816 None => FlexboxAxisRelation::Unknown,
817 Some(dir) => {
818 let is_main = matches!(
819 (dir, orientation),
820 (
821 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse,
822 Orientation::Horizontal
823 ) | (
824 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse,
825 Orientation::Vertical
826 )
827 );
828 if is_main { FlexboxAxisRelation::MainAxis } else { FlexboxAxisRelation::CrossAxis }
829 }
830 }
831 }
832
833 pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
834 for cell in &mut self.elems {
835 cell.constraints.visit_named_references(visitor);
836 if let Some(e) = cell.cross_axis_self_alignment.as_mut() {
837 visitor(&mut *e)
838 }
839 if let Some(e) = cell.layout_order.as_mut() {
840 visitor(&mut *e)
841 }
842 }
843 self.geometry.visit_named_references(visitor);
844 if let Some(e) = self.direction.as_mut() {
845 visitor(&mut *e)
846 }
847 if let Some(e) = self.cross_axis_line_alignment.as_mut() {
848 visitor(&mut *e)
849 }
850 if let Some(e) = self.cross_axis_alignment.as_mut() {
851 visitor(&mut *e)
852 }
853 if let Some(e) = self.flex_wrap.as_mut() {
854 visitor(&mut *e)
855 }
856 }
857}
858
859fn has_no_intrinsic_size(base: &ElementType) -> bool {
864 let name = match base {
865 ElementType::Builtin(b) => b.name.as_str(),
866 ElementType::Native(n) => n.class_name.as_str(),
867 _ => return false,
868 };
869 matches!(
870 name,
871 "Rectangle"
872 | "BasicBorderRectangle"
873 | "BorderRectangle"
874 | "Empty"
875 | "TouchArea"
876 | "FocusScope"
877 | "Opacity"
878 | "Layer"
879 | "BoxShadow"
880 | "Clip"
881 )
882}
883
884#[derive(Clone, Copy, PartialEq)]
887pub enum BuiltinFilter {
888 All,
890 SkipNonImplicit,
892}
893
894pub fn implicit_layout_info_call(
898 elem: &ElementRc,
899 orientation: Orientation,
900 filter: BuiltinFilter,
901 constraint: Option<Expression>,
902) -> Option<Expression> {
903 let mut elem_it = elem.clone();
904 let height_settled = elem.borrow().height_is_literal;
907 loop {
908 return match &elem_it.clone().borrow().base_type {
909 ElementType::Component(base_comp) => {
910 let parametrized_nr =
914 constraint.as_ref().filter(|_| orientation == Orientation::Vertical).and_then(
915 |_| base_comp.root_element.borrow().layout_info_v_with_constraint.clone(),
916 );
917 if let Some(nr) = parametrized_nr
918 && let Some(c) = &constraint
919 {
920 debug_assert!(Rc::ptr_eq(&nr.element(), &base_comp.root_element));
921 return Some(Expression::FunctionCall {
922 function: crate::expression_tree::Callable::Function(NamedReference::new(
923 elem,
924 nr.name().clone(),
925 )),
926 arguments: vec![c.clone()],
927 source_location: None,
928 });
929 }
930 let base_prop = elem_it.borrow().base_layout_info_prop(orientation, height_settled);
931 match base_prop {
932 Some(nr) => {
933 debug_assert!(Rc::ptr_eq(&nr.element(), &base_comp.root_element));
936 Some(Expression::PropertyReference(NamedReference::new(
937 elem,
938 nr.name().clone(),
939 )))
940 }
941 None => {
942 elem_it = base_comp.root_element.clone();
943 continue;
944 }
945 }
946 }
947 base @ (ElementType::Builtin(_) | ElementType::Native(_))
948 if has_no_intrinsic_size(base) =>
949 {
950 if filter == BuiltinFilter::SkipNonImplicit {
951 return None;
952 }
953 Some(Expression::Struct {
956 ty: crate::typeregister::layout_info_type(),
957 values: [("min", 0.), ("max", f32::MAX), ("preferred", 0.)]
958 .iter()
959 .map(|(s, v)| {
960 (SmolStr::new_static(s), Expression::NumberLiteral(*v as _, Unit::Px))
961 })
962 .chain(
963 [("min_percent", 0.), ("max_percent", 100.), ("stretch", 1.)]
964 .iter()
965 .map(|(s, v)| {
966 (
967 SmolStr::new_static(s),
968 Expression::NumberLiteral(*v, Unit::None),
969 )
970 }),
971 )
972 .collect(),
973 })
974 }
975 ElementType::Builtin(base_type)
976 if filter == BuiltinFilter::SkipNonImplicit
977 && base_type.default_size_binding
978 != crate::langtype::DefaultSizeBinding::ImplicitSize =>
979 {
980 None
981 }
982 _ => Some(Expression::FunctionCall {
983 function: BuiltinFunction::ImplicitLayoutInfo(orientation).into(),
984 arguments: vec![
985 Expression::ElementReference(Rc::downgrade(elem)),
986 constraint.unwrap_or(Expression::NumberLiteral(-1., Unit::None)),
987 ],
988 source_location: None,
989 }),
990 };
991 }
992}
993
994pub fn static_native_stretch(elem: &ElementRc) -> Option<Expression> {
998 elem.borrow()
999 .builtin_type()
1000 .filter(|b| matches!(b.name.as_str(), "Text" | "StyledText" | "TextInput" | "Image"))
1001 .map(|_| Expression::NumberLiteral(0., Unit::None))
1002}
1003
1004pub fn create_new_prop(elem: &ElementRc, tentative_name: SmolStr, ty: Type) -> NamedReference {
1006 let mut e = elem.borrow_mut();
1007 let name = if e.lookup_property(&tentative_name, PropertyLookupMode::InternalName).is_valid() {
1008 e.unique_member_name(&tentative_name)
1009 } else {
1010 tentative_name
1011 };
1012 e.property_declarations.insert(name.clone(), ty.into());
1013 drop(e);
1014 NamedReference::new(elem, name)
1015}
1016
1017pub fn is_layout(base_type: &ElementType) -> bool {
1019 match base_type {
1020 ElementType::Component(c) => is_layout(&c.root_element.borrow().base_type),
1021 ElementType::Builtin(be) => {
1022 matches!(
1023 be.name.as_str(),
1024 "GridLayout" | "HorizontalLayout" | "VerticalLayout" | "FlexboxLayout"
1025 )
1026 }
1027 _ => false,
1028 }
1029}