use crate::diagnostics::{BuildDiagnostics, DiagnosticLevel, Spanned};
use crate::expression_tree::*;
use crate::langtype::{ElementType, PropertyLookupMode, PropertyLookupResult, Type};
use crate::object_tree::{Component, ElementRc};
use smol_str::{SmolStr, ToSmolStr};
use std::cell::RefCell;
use std::rc::{Rc, Weak};
pub const BOX_LAYOUT_CACHE_ENTRIES_PER_CELL: usize = 2;
#[derive(Clone, Debug, Copy, Eq, PartialEq)]
pub enum Orientation {
Horizontal,
Vertical,
}
impl Orientation {
pub fn orthogonal(self) -> Self {
match self {
Orientation::Horizontal => Orientation::Vertical,
Orientation::Vertical => Orientation::Horizontal,
}
}
}
#[derive(Clone, Debug, Copy, Eq, PartialEq, Default)]
pub enum FlexboxLayoutDirection {
#[default]
Row,
RowReverse,
Column,
ColumnReverse,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FlexboxAxisRelation {
MainAxis,
CrossAxis,
Unknown,
}
#[derive(Clone, Debug, derive_more::From)]
pub enum Layout {
GridLayout(GridLayout),
BoxLayout(BoxLayout),
FlexboxLayout(FlexboxLayout),
}
impl Layout {
pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
match self {
Layout::GridLayout(grid) => grid.visit_named_references(visitor),
Layout::BoxLayout(l) => l.visit_named_references(visitor),
Layout::FlexboxLayout(l) => l.visit_named_references(visitor),
}
}
}
#[derive(Debug, Default, Clone)]
pub struct LayoutItem {
pub element: ElementRc,
pub constraints: LayoutConstraints,
pub cross_axis_self_alignment: Option<NamedReference>,
pub layout_order: Option<NamedReference>,
}
#[derive(Debug, Clone)]
pub enum RowChildTemplate {
Static(LayoutItem),
Repeated {
item: LayoutItem,
repeated_element: ElementRc,
},
}
impl RowChildTemplate {
pub fn layout_item(&self) -> &LayoutItem {
match self {
RowChildTemplate::Static(item) => item,
RowChildTemplate::Repeated { item, .. } => item,
}
}
pub fn layout_item_mut(&mut self) -> &mut LayoutItem {
match self {
RowChildTemplate::Static(item) => item,
RowChildTemplate::Repeated { item, .. } => item,
}
}
pub fn repeated_element(&self) -> Option<&ElementRc> {
match self {
RowChildTemplate::Static(_) => None,
RowChildTemplate::Repeated { repeated_element, .. } => Some(repeated_element),
}
}
pub fn is_repeated(&self) -> bool {
self.repeated_element().is_some()
}
}
impl LayoutItem {
pub fn rect(&self) -> LayoutRect {
let p = |unresolved_name: &str| {
let PropertyLookupResult { resolved_name, property_type, .. } = self
.element
.borrow()
.lookup_property(unresolved_name, PropertyLookupMode::ComponentLocal);
if property_type == Type::LogicalLength {
Some(NamedReference::new(&self.element, resolved_name.to_smolstr()))
} else {
None
}
};
LayoutRect {
x_reference: p("x"),
y_reference: p("y"),
width_reference: if !self.constraints.fixed_width { p("width") } else { None },
height_reference: if !self.constraints.fixed_height { p("height") } else { None },
}
}
}
#[derive(Debug, Clone, Default)]
pub struct LayoutRect {
pub width_reference: Option<NamedReference>,
pub height_reference: Option<NamedReference>,
pub x_reference: Option<NamedReference>,
pub y_reference: Option<NamedReference>,
}
impl LayoutRect {
pub fn install_on_element(element: &ElementRc) -> Self {
let install_prop =
|name: &'static str| Some(NamedReference::new(element, SmolStr::new_static(name)));
Self {
x_reference: install_prop("x"),
y_reference: install_prop("y"),
width_reference: install_prop("width"),
height_reference: install_prop("height"),
}
}
fn visit_named_references(&mut self, mut visitor: &mut dyn FnMut(&mut NamedReference)) {
self.width_reference.as_mut().map(&mut visitor);
self.height_reference.as_mut().map(&mut visitor);
self.x_reference.as_mut().map(&mut visitor);
self.y_reference.as_mut().map(&mut visitor);
}
pub fn size_reference(&self, orientation: Orientation) -> Option<&NamedReference> {
match orientation {
Orientation::Horizontal => self.width_reference.as_ref(),
Orientation::Vertical => self.height_reference.as_ref(),
}
}
}
#[derive(Debug, Default, Clone)]
pub struct LayoutConstraints {
pub min_width: Option<NamedReference>,
pub max_width: Option<NamedReference>,
pub min_height: Option<NamedReference>,
pub max_height: Option<NamedReference>,
pub preferred_width: Option<NamedReference>,
pub preferred_height: Option<NamedReference>,
pub horizontal_stretch: Option<NamedReference>,
pub vertical_stretch: Option<NamedReference>,
pub fixed_width: bool,
pub fixed_height: bool,
pub local: LayoutConstraintLocality,
}
#[derive(Debug, Default, Clone)]
pub struct LayoutConstraintLocality {
pub min_width: bool,
pub max_width: bool,
pub min_height: bool,
pub max_height: bool,
pub preferred_width: bool,
pub preferred_height: bool,
pub horizontal_stretch: bool,
pub vertical_stretch: bool,
}
pub struct OrientationConstraints<'a> {
pub min: &'a Option<NamedReference>,
pub max: &'a Option<NamedReference>,
pub preferred: &'a Option<NamedReference>,
pub stretch: &'a Option<NamedReference>,
pub fixed: bool,
}
impl LayoutConstraints {
pub fn new(
element: &ElementRc,
mut diag: Option<(&mut BuildDiagnostics, DiagnosticLevel)>,
) -> Self {
let mut constraints = Self {
min_width: binding_reference(element, "min-width"),
max_width: binding_reference(element, "max-width"),
min_height: binding_reference(element, "min-height"),
max_height: binding_reference(element, "max-height"),
preferred_width: binding_reference(element, "preferred-width"),
preferred_height: binding_reference(element, "preferred-height"),
horizontal_stretch: binding_reference(element, "horizontal-stretch"),
vertical_stretch: binding_reference(element, "vertical-stretch"),
fixed_width: false,
fixed_height: false,
local: LayoutConstraintLocality {
min_width: is_local_binding(element, "min-width")
|| is_local_binding(element, "width"),
max_width: is_local_binding(element, "max-width")
|| is_local_binding(element, "width"),
min_height: is_local_binding(element, "min-height")
|| is_local_binding(element, "height"),
max_height: is_local_binding(element, "max-height")
|| is_local_binding(element, "height"),
preferred_width: is_local_binding(element, "preferred-width"),
preferred_height: is_local_binding(element, "preferred-height"),
horizontal_stretch: is_local_binding(element, "horizontal-stretch"),
vertical_stretch: is_local_binding(element, "vertical-stretch"),
},
};
let mut apply_size_constraint =
|prop: &'static str,
binding: &BindingExpression,
enclosing1: &Weak<Component>,
depth,
op: &mut Option<NamedReference>| {
if let Some(other_prop) = op {
find_binding(
&other_prop.element(),
other_prop.name(),
|old, enclosing2, d2| {
if let Some((diag, level)) = &mut diag
&& Weak::ptr_eq(enclosing1, enclosing2)
&& old.priority.saturating_add(d2)
<= binding.priority.saturating_add(depth)
{
diag.push_diagnostic_with_span(
format!(
"Cannot specify both '{prop}' and '{}'",
other_prop.name()
),
binding.to_source_location(),
*level,
);
}
},
);
}
*op = Some(NamedReference::new(element, SmolStr::new_static(prop)))
};
find_binding(element, "height", |s, enclosing, depth| {
constraints.fixed_height = true;
apply_size_constraint("height", s, enclosing, depth, &mut constraints.min_height);
apply_size_constraint("height", s, enclosing, depth, &mut constraints.max_height);
});
find_binding(element, "width", |s, enclosing, depth| {
constraints.fixed_width = true;
if s.expression.ty() == Type::Percent {
apply_size_constraint("width", s, enclosing, depth, &mut constraints.min_width);
} else {
apply_size_constraint("width", s, enclosing, depth, &mut constraints.min_width);
apply_size_constraint("width", s, enclosing, depth, &mut constraints.max_width);
}
});
constraints
}
pub fn has_explicit_restrictions(&self, orientation: Orientation) -> bool {
match orientation {
Orientation::Horizontal => {
self.min_width.is_some()
|| self.max_width.is_some()
|| self.preferred_width.is_some()
|| self.horizontal_stretch.is_some()
}
Orientation::Vertical => {
self.min_height.is_some()
|| self.max_height.is_some()
|| self.preferred_height.is_some()
|| self.vertical_stretch.is_some()
}
}
}
pub fn to_apply(&self, element: &ElementRc, orientation: Orientation) -> Self {
if !element.borrow().layout_info_includes_own_constraints(orientation) {
return self.clone();
}
let mut c = self.clone();
match orientation {
Orientation::Horizontal => {
if !self.local.min_width {
c.min_width = None;
}
if !self.local.max_width {
c.max_width = None;
}
if !self.local.preferred_width {
c.preferred_width = None;
}
if !self.local.horizontal_stretch {
c.horizontal_stretch = None;
}
}
Orientation::Vertical => {
if !self.local.min_height {
c.min_height = None;
}
if !self.local.max_height {
c.max_height = None;
}
if !self.local.preferred_height {
c.preferred_height = None;
}
if !self.local.vertical_stretch {
c.vertical_stretch = None;
}
}
}
c
}
pub fn for_orientation(&self, orientation: Orientation) -> OrientationConstraints<'_> {
match orientation {
Orientation::Horizontal => OrientationConstraints {
min: &self.min_width,
max: &self.max_width,
preferred: &self.preferred_width,
stretch: &self.horizontal_stretch,
fixed: self.fixed_width,
},
Orientation::Vertical => OrientationConstraints {
min: &self.min_height,
max: &self.max_height,
preferred: &self.preferred_height,
stretch: &self.vertical_stretch,
fixed: self.fixed_height,
},
}
}
pub fn for_each_restrictions(
&self,
orientation: Orientation,
) -> impl Iterator<Item = (&NamedReference, &'static str)> {
let c = self.for_orientation(orientation);
std::iter::empty()
.chain(c.min.as_ref().map(|x| {
if Expression::PropertyReference(x.clone()).ty() != Type::Percent {
(x, "min")
} else {
(x, "min_percent")
}
}))
.chain(c.max.as_ref().map(|x| {
if Expression::PropertyReference(x.clone()).ty() != Type::Percent {
(x, "max")
} else {
(x, "max_percent")
}
}))
.chain(c.preferred.as_ref().map(|x| (x, "preferred")))
.chain(c.stretch.as_ref().map(|x| (x, "stretch")))
}
pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
if let Some(e) = self.max_width.as_mut() {
visitor(&mut *e);
}
if let Some(e) = self.min_width.as_mut() {
visitor(&mut *e);
}
if let Some(e) = self.max_height.as_mut() {
visitor(&mut *e);
}
if let Some(e) = self.min_height.as_mut() {
visitor(&mut *e);
}
if let Some(e) = self.preferred_width.as_mut() {
visitor(&mut *e);
}
if let Some(e) = self.preferred_height.as_mut() {
visitor(&mut *e);
}
if let Some(e) = self.horizontal_stretch.as_mut() {
visitor(&mut *e);
}
if let Some(e) = self.vertical_stretch.as_mut() {
visitor(&mut *e);
}
}
}
#[derive(Debug, Clone)]
pub enum RowColExpr {
Named(NamedReference),
Literal(u16),
Auto,
}
#[derive(Debug, Clone)]
pub struct GridLayoutCell {
pub new_row: bool,
pub col_expr: RowColExpr,
pub row_expr: RowColExpr,
pub colspan_expr: RowColExpr,
pub rowspan_expr: RowColExpr,
pub child_items: Option<Vec<RowChildTemplate>>, }
impl GridLayoutCell {
pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
if let RowColExpr::Named(ref mut e) = self.col_expr {
visitor(e);
}
if let RowColExpr::Named(ref mut e) = self.row_expr {
visitor(e);
}
if let RowColExpr::Named(ref mut e) = self.colspan_expr {
visitor(e);
}
if let RowColExpr::Named(ref mut e) = self.rowspan_expr {
visitor(e);
}
if let Some(children) = &mut self.child_items {
for child in children {
child.layout_item_mut().constraints.visit_named_references(visitor);
}
}
}
}
#[derive(Debug, Clone)]
pub struct GridLayoutElement {
pub cell: Rc<RefCell<GridLayoutCell>>,
pub item: LayoutItem,
}
impl GridLayoutElement {
pub fn span(&self, orientation: Orientation) -> RowColExpr {
let cell = self.cell.borrow();
match orientation {
Orientation::Horizontal => cell.colspan_expr.clone(),
Orientation::Vertical => cell.rowspan_expr.clone(),
}
}
}
#[derive(Debug, Clone)]
pub struct Padding {
pub left: Option<NamedReference>,
pub right: Option<NamedReference>,
pub top: Option<NamedReference>,
pub bottom: Option<NamedReference>,
}
impl Padding {
fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
if let Some(e) = self.left.as_mut() {
visitor(&mut *e)
}
if let Some(e) = self.right.as_mut() {
visitor(&mut *e)
}
if let Some(e) = self.top.as_mut() {
visitor(&mut *e)
}
if let Some(e) = self.bottom.as_mut() {
visitor(&mut *e)
}
}
pub fn begin_end(&self, o: Orientation) -> (Option<&NamedReference>, Option<&NamedReference>) {
match o {
Orientation::Horizontal => (self.left.as_ref(), self.right.as_ref()),
Orientation::Vertical => (self.top.as_ref(), self.bottom.as_ref()),
}
}
}
#[derive(Debug, Clone)]
pub struct Spacing {
pub horizontal: Option<NamedReference>,
pub vertical: Option<NamedReference>,
}
impl Spacing {
fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
if let Some(e) = self.horizontal.as_mut() {
visitor(&mut *e);
}
if let Some(e) = self.vertical.as_mut() {
visitor(&mut *e);
}
}
pub fn orientation(&self, o: Orientation) -> Option<&NamedReference> {
match o {
Orientation::Horizontal => self.horizontal.as_ref(),
Orientation::Vertical => self.vertical.as_ref(),
}
}
}
#[derive(Debug, Clone)]
pub struct LayoutGeometry {
pub rect: LayoutRect,
pub spacing: Spacing,
pub alignment: Option<NamedReference>,
pub padding: Padding,
}
impl LayoutGeometry {
pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
self.rect.visit_named_references(visitor);
if let Some(e) = self.alignment.as_mut() {
visitor(&mut *e)
}
self.spacing.visit_named_references(visitor);
self.padding.visit_named_references(visitor);
}
pub fn new(layout_element: &ElementRc) -> Self {
let spacing = || binding_reference(layout_element, "spacing");
init_fake_property(layout_element, "spacing-horizontal", spacing);
init_fake_property(layout_element, "spacing-vertical", spacing);
let alignment = binding_reference(layout_element, "alignment");
let padding = || binding_reference(layout_element, "padding");
init_fake_property(layout_element, "padding-left", padding);
init_fake_property(layout_element, "padding-right", padding);
init_fake_property(layout_element, "padding-top", padding);
init_fake_property(layout_element, "padding-bottom", padding);
let padding = Padding {
left: binding_reference(layout_element, "padding-left").or_else(padding),
right: binding_reference(layout_element, "padding-right").or_else(padding),
top: binding_reference(layout_element, "padding-top").or_else(padding),
bottom: binding_reference(layout_element, "padding-bottom").or_else(padding),
};
let spacing = Spacing {
horizontal: binding_reference(layout_element, "spacing-horizontal").or_else(spacing),
vertical: binding_reference(layout_element, "spacing-vertical").or_else(spacing),
};
let rect = LayoutRect::install_on_element(layout_element);
Self { rect, spacing, padding, alignment }
}
}
pub(crate) fn find_binding<R>(
element: &ElementRc,
name: &str,
f: impl FnOnce(&BindingExpression, &Weak<Component>, i32) -> R,
) -> Option<R> {
let mut element = element.clone();
let mut depth = 0;
loop {
if let Some(b) = element.borrow().binding(name)
&& b.has_binding()
{
return Some(f(&b, &element.borrow().enclosing_component, depth));
}
let e = match &element.borrow().base_type {
ElementType::Component(base) => base.root_element.clone(),
_ => return None,
};
element = e;
depth += 1;
}
}
pub fn binding_reference(element: &ElementRc, name: &'static str) -> Option<NamedReference> {
find_binding(element, name, |_, _, _| NamedReference::new(element, SmolStr::new_static(name)))
}
fn is_local_binding(element: &ElementRc, name: &str) -> bool {
find_binding(element, name, |_, _, depth| depth == 0) == Some(true)
}
fn init_fake_property(
grid_layout_element: &ElementRc,
name: &str,
lazy_default: impl Fn() -> Option<NamedReference>,
) {
if grid_layout_element.borrow().property_declarations.contains_key(name)
&& grid_layout_element.borrow().binding(name).is_none()
&& let Some(e) = lazy_default()
{
if e.name() == name && Rc::ptr_eq(&e.element(), grid_layout_element) {
return;
}
grid_layout_element
.borrow_mut()
.set_binding(name.into(), Expression::PropertyReference(e).into());
}
}
#[derive(Debug, Clone)]
pub struct GridLayout {
pub elems: Vec<GridLayoutElement>,
pub geometry: LayoutGeometry,
pub dialog_button_roles: Option<Vec<SmolStr>>,
pub uses_auto: bool,
}
impl GridLayout {
pub fn clone_cells(&mut self) {
for e in &mut self.elems {
let cloned = Rc::new(RefCell::new(e.cell.borrow().clone()));
e.cell = cloned;
}
}
pub fn visit_rowcol_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
for elem in &mut self.elems {
let mut cell = elem.cell.borrow_mut();
if let RowColExpr::Named(ref mut e) = cell.col_expr {
visitor(e);
}
if let RowColExpr::Named(ref mut e) = cell.row_expr {
visitor(e);
}
if let RowColExpr::Named(ref mut e) = cell.colspan_expr {
visitor(e);
}
if let RowColExpr::Named(ref mut e) = cell.rowspan_expr {
visitor(e);
}
}
}
pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
self.visit_rowcol_named_references(visitor);
for layout_elem in &mut self.elems {
layout_elem.item.constraints.visit_named_references(visitor);
if let Some(child_items) = &mut layout_elem.cell.borrow_mut().child_items {
for child in child_items {
child.layout_item_mut().constraints.visit_named_references(visitor);
}
}
}
self.geometry.visit_named_references(visitor);
}
}
#[derive(Debug, Clone)]
pub struct BoxLayout {
pub orientation: Orientation,
pub elems: Vec<LayoutItem>,
pub geometry: LayoutGeometry,
pub cross_alignment: Option<NamedReference>,
}
impl BoxLayout {
pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
for cell in &mut self.elems {
cell.constraints.visit_named_references(visitor);
if let Some(e) = cell.cross_axis_self_alignment.as_mut() {
visitor(&mut *e);
}
if let Some(e) = cell.layout_order.as_mut() {
visitor(&mut *e);
}
}
self.geometry.visit_named_references(visitor);
if let Some(e) = self.cross_alignment.as_mut() {
visitor(&mut *e);
}
}
}
#[derive(Debug, Clone)]
pub struct FlexboxLayout {
pub elems: Vec<LayoutItem>,
pub geometry: LayoutGeometry,
pub direction: Option<NamedReference>,
pub cross_axis_line_alignment: Option<NamedReference>,
pub cross_axis_alignment: Option<NamedReference>,
pub flex_wrap: Option<NamedReference>,
}
impl FlexboxLayout {
pub fn from_element(elem: &ElementRc) -> Option<FlexboxLayout> {
use crate::expression_tree::Expression;
let nr = {
let eb = elem.borrow();
eb.effective_layout_info_prop(Orientation::Vertical)
.or_else(|| eb.effective_layout_info_prop(Orientation::Horizontal))
.cloned()
}?;
let target = nr.element();
let target = target.borrow();
let binding = target.binding(nr.name())?;
match binding.value_expression() {
Expression::ComputeFlexboxLayoutInfo { layout, .. } => Some(layout.clone()),
_ => None,
}
}
fn compile_time_direction(&self) -> Option<FlexboxLayoutDirection> {
match self.direction.as_ref() {
None => Some(FlexboxLayoutDirection::Row),
Some(nr) => nr.element().borrow().binding(nr.name()).and_then(|binding| {
if let crate::expression_tree::Expression::EnumerationValue(ev) =
binding.value_expression()
{
match ev.enumeration.values[ev.value].as_str() {
"row" => Some(FlexboxLayoutDirection::Row),
"row-reverse" => Some(FlexboxLayoutDirection::RowReverse),
"column" => Some(FlexboxLayoutDirection::Column),
"column-reverse" => Some(FlexboxLayoutDirection::ColumnReverse),
_ => None,
}
} else {
None
}
}),
}
}
pub fn axis_relation(&self, orientation: Orientation) -> FlexboxAxisRelation {
match self.compile_time_direction() {
None => FlexboxAxisRelation::Unknown,
Some(dir) => {
let is_main = matches!(
(dir, orientation),
(
FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse,
Orientation::Horizontal
) | (
FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse,
Orientation::Vertical
)
);
if is_main { FlexboxAxisRelation::MainAxis } else { FlexboxAxisRelation::CrossAxis }
}
}
}
pub fn visit_named_references(&mut self, visitor: &mut dyn FnMut(&mut NamedReference)) {
for cell in &mut self.elems {
cell.constraints.visit_named_references(visitor);
if let Some(e) = cell.cross_axis_self_alignment.as_mut() {
visitor(&mut *e)
}
if let Some(e) = cell.layout_order.as_mut() {
visitor(&mut *e)
}
}
self.geometry.visit_named_references(visitor);
if let Some(e) = self.direction.as_mut() {
visitor(&mut *e)
}
if let Some(e) = self.cross_axis_line_alignment.as_mut() {
visitor(&mut *e)
}
if let Some(e) = self.cross_axis_alignment.as_mut() {
visitor(&mut *e)
}
if let Some(e) = self.flex_wrap.as_mut() {
visitor(&mut *e)
}
}
}
fn has_no_intrinsic_size(base: &ElementType) -> bool {
let name = match base {
ElementType::Builtin(b) => b.name.as_str(),
ElementType::Native(n) => n.class_name.as_str(),
_ => return false,
};
matches!(
name,
"Rectangle"
| "BasicBorderRectangle"
| "BorderRectangle"
| "Empty"
| "TouchArea"
| "FocusScope"
| "Opacity"
| "Layer"
| "BoxShadow"
| "Clip"
)
}
#[derive(Clone, Copy, PartialEq)]
pub enum BuiltinFilter {
All,
SkipNonImplicit,
}
pub fn implicit_layout_info_call(
elem: &ElementRc,
orientation: Orientation,
filter: BuiltinFilter,
constraint: Option<Expression>,
) -> Option<Expression> {
let mut elem_it = elem.clone();
let height_settled = elem.borrow().height_is_literal;
loop {
return match &elem_it.clone().borrow().base_type {
ElementType::Component(base_comp) => {
let parametrized_nr =
constraint.as_ref().filter(|_| orientation == Orientation::Vertical).and_then(
|_| base_comp.root_element.borrow().layout_info_v_with_constraint.clone(),
);
if let Some(nr) = parametrized_nr
&& let Some(c) = &constraint
{
debug_assert!(Rc::ptr_eq(&nr.element(), &base_comp.root_element));
return Some(Expression::FunctionCall {
function: crate::expression_tree::Callable::Function(NamedReference::new(
elem,
nr.name().clone(),
)),
arguments: vec![c.clone()],
source_location: None,
});
}
let base_prop = elem_it.borrow().base_layout_info_prop(orientation, height_settled);
match base_prop {
Some(nr) => {
debug_assert!(Rc::ptr_eq(&nr.element(), &base_comp.root_element));
Some(Expression::PropertyReference(NamedReference::new(
elem,
nr.name().clone(),
)))
}
None => {
elem_it = base_comp.root_element.clone();
continue;
}
}
}
base @ (ElementType::Builtin(_) | ElementType::Native(_))
if has_no_intrinsic_size(base) =>
{
if filter == BuiltinFilter::SkipNonImplicit {
return None;
}
Some(Expression::Struct {
ty: crate::typeregister::layout_info_type(),
values: [("min", 0.), ("max", f32::MAX), ("preferred", 0.)]
.iter()
.map(|(s, v)| {
(SmolStr::new_static(s), Expression::NumberLiteral(*v as _, Unit::Px))
})
.chain(
[("min_percent", 0.), ("max_percent", 100.), ("stretch", 1.)]
.iter()
.map(|(s, v)| {
(
SmolStr::new_static(s),
Expression::NumberLiteral(*v, Unit::None),
)
}),
)
.collect(),
})
}
ElementType::Builtin(base_type)
if filter == BuiltinFilter::SkipNonImplicit
&& base_type.default_size_binding
!= crate::langtype::DefaultSizeBinding::ImplicitSize =>
{
None
}
_ => Some(Expression::FunctionCall {
function: BuiltinFunction::ImplicitLayoutInfo(orientation).into(),
arguments: vec![
Expression::ElementReference(Rc::downgrade(elem)),
constraint.unwrap_or(Expression::NumberLiteral(-1., Unit::None)),
],
source_location: None,
}),
};
}
}
pub fn static_native_stretch(elem: &ElementRc) -> Option<Expression> {
elem.borrow()
.builtin_type()
.filter(|b| matches!(b.name.as_str(), "Text" | "StyledText" | "TextInput" | "Image"))
.map(|_| Expression::NumberLiteral(0., Unit::None))
}
pub fn create_new_prop(elem: &ElementRc, tentative_name: SmolStr, ty: Type) -> NamedReference {
let mut e = elem.borrow_mut();
let name = if e.lookup_property(&tentative_name, PropertyLookupMode::InternalName).is_valid() {
e.unique_member_name(&tentative_name)
} else {
tentative_name
};
e.property_declarations.insert(name.clone(), ty.into());
drop(e);
NamedReference::new(elem, name)
}
pub fn is_layout(base_type: &ElementType) -> bool {
match base_type {
ElementType::Component(c) => is_layout(&c.root_element.borrow().base_type),
ElementType::Builtin(be) => {
matches!(
be.name.as_str(),
"GridLayout" | "HorizontalLayout" | "VerticalLayout" | "FlexboxLayout"
)
}
_ => false,
}
}