slint/internal/compiler/passes/lower_layout.rs
Olivier Goffart 31502d5918 VSCode web extension: load files from the vscode API
We can't use file system call to load files with wasm, we need to go through the vscode API

Note that this is all async, so i had to prevent re-entry within the compiler using a rentry trick.

I removed the lifetime in the TypeLoader because I thought this was going to be necessary
to get 'static futures. But it turned out not to be necessary. Anyway, I kept it htis way because
it is actually nicer IMHO, even if the CompilationConfiguration is now copied
2022-06-02 16:21:07 +02:00

803 lines
30 KiB
Rust

// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
//! Passe that compute the layout constraint
use lyon_path::geom::euclid::approxeq::ApproxEq;
use crate::diagnostics::BuildDiagnostics;
use crate::diagnostics::Spanned;
use crate::expression_tree::*;
use crate::langtype::Type;
use crate::layout::*;
use crate::object_tree::*;
use crate::typeloader::TypeLoader;
use crate::typeregister::TypeRegister;
use std::cell::RefCell;
use std::collections::HashSet;
use std::rc::Rc;
pub async fn lower_layouts(
component: &Rc<Component>,
type_loader: &mut TypeLoader,
diag: &mut BuildDiagnostics,
) {
// Ignore import errors
let mut build_diags_to_ignore = crate::diagnostics::BuildDiagnostics::default();
let style_metrics = type_loader
.import_type("std-widgets.slint", "StyleMetrics", &mut build_diags_to_ignore)
.await;
let style_metrics =
style_metrics.and_then(|sm| if let Type::Component(c) = sm { Some(c) } else { None });
*component.root_constraints.borrow_mut() =
LayoutConstraints::new(&component.root_element, diag);
recurse_elem_including_sub_components(component, &(), &mut |elem, _| {
let component = elem.borrow().enclosing_component.upgrade().unwrap();
lower_element_layout(
&component,
elem,
&type_loader.global_type_registry.borrow(),
&style_metrics,
diag,
);
check_no_layout_properties(elem, diag);
});
}
fn lower_element_layout(
component: &Rc<Component>,
elem: &ElementRc,
type_register: &TypeRegister,
style_metrics: &Option<Rc<Component>>,
diag: &mut BuildDiagnostics,
) {
let base_type = if let Type::Builtin(base_type) = &elem.borrow().base_type {
base_type.clone()
} else {
return;
};
match base_type.name.as_str() {
"Row" => panic!("Error caught at element lookup time"),
"GridLayout" => lower_grid_layout(component, elem, diag),
"HorizontalLayout" => lower_box_layout(elem, diag, Orientation::Horizontal),
"VerticalLayout" => lower_box_layout(elem, diag, Orientation::Vertical),
"PathLayout" => lower_path_layout(elem, diag),
"Dialog" => {
lower_dialog_layout(elem, style_metrics, diag);
return; // the Dialog stays in the tree as a Dialog
}
_ => return,
};
{
let mut elem = elem.borrow_mut();
let elem = &mut *elem;
let prev_base = std::mem::replace(&mut elem.base_type, type_register.lookup("Rectangle"));
// Create fake properties for the layout properties
for p in elem.bindings.keys() {
if !elem.base_type.lookup_property(p).is_valid()
&& !elem.property_declarations.contains_key(p)
{
let ty = prev_base.lookup_property(p).property_type;
if ty != Type::Invalid {
elem.property_declarations.insert(p.into(), ty.into());
}
}
}
}
}
pub fn is_layout_element(element: &ElementRc) -> bool {
matches!(&element.borrow().base_type, Type::Builtin(n) if n.name == "GridLayout" || n.name == "HorizontalLayout" || n.name == "VerticalLayout" || n.name == "PathLayout")
}
fn lower_grid_layout(
component: &Rc<Component>,
grid_layout_element: &ElementRc,
diag: &mut BuildDiagnostics,
) {
let mut grid = GridLayout {
elems: Default::default(),
geometry: LayoutGeometry::new(grid_layout_element),
dialog_button_roles: None,
};
let layout_cache_prop_h =
create_new_prop(grid_layout_element, "layout-cache-h", Type::LayoutCache);
let layout_cache_prop_v =
create_new_prop(grid_layout_element, "layout-cache-v", Type::LayoutCache);
let layout_info_prop_h =
create_new_prop(grid_layout_element, "layoutinfo-h", layout_info_type());
let layout_info_prop_v =
create_new_prop(grid_layout_element, "layoutinfo-v", layout_info_type());
let mut row = 0;
let mut col = 0;
let layout_children = std::mem::take(&mut grid_layout_element.borrow_mut().children);
let mut collected_children = Vec::new();
for layout_child in layout_children {
let is_row = if let Type::Builtin(be) = &layout_child.borrow().base_type {
be.name == "Row"
} else {
false
};
if is_row {
if col > 0 {
row += 1;
col = 0;
}
let row_children = std::mem::take(&mut layout_child.borrow_mut().children);
for x in row_children {
grid.add_element(
&x,
(&mut row, &mut col),
&layout_cache_prop_h,
&layout_cache_prop_v,
diag,
);
col += 1;
collected_children.push(x);
}
if col > 0 {
row += 1;
col = 0;
}
component.optimized_elements.borrow_mut().push(layout_child);
} else {
grid.add_element(
&layout_child,
(&mut row, &mut col),
&layout_cache_prop_h,
&layout_cache_prop_v,
diag,
);
col += 1;
collected_children.push(layout_child);
}
}
grid_layout_element.borrow_mut().children = collected_children;
let span = grid_layout_element.borrow().to_source_location();
layout_cache_prop_h.element().borrow_mut().bindings.insert(
layout_cache_prop_h.name().into(),
BindingExpression::new_with_span(
Expression::SolveLayout(Layout::GridLayout(grid.clone()), Orientation::Horizontal),
span.clone(),
)
.into(),
);
layout_cache_prop_v.element().borrow_mut().bindings.insert(
layout_cache_prop_v.name().into(),
BindingExpression::new_with_span(
Expression::SolveLayout(Layout::GridLayout(grid.clone()), Orientation::Vertical),
span.clone(),
)
.into(),
);
layout_info_prop_h.element().borrow_mut().bindings.insert(
layout_info_prop_h.name().into(),
BindingExpression::new_with_span(
Expression::ComputeLayoutInfo(
Layout::GridLayout(grid.clone()),
Orientation::Horizontal,
),
span.clone(),
)
.into(),
);
layout_info_prop_v.element().borrow_mut().bindings.insert(
layout_info_prop_v.name().into(),
BindingExpression::new_with_span(
Expression::ComputeLayoutInfo(Layout::GridLayout(grid), Orientation::Vertical),
span,
)
.into(),
);
grid_layout_element.borrow_mut().layout_info_prop =
Some((layout_info_prop_h, layout_info_prop_v));
}
impl GridLayout {
fn add_element(
&mut self,
item_element: &ElementRc,
(row, col): (&mut u16, &mut u16),
layout_cache_prop_h: &NamedReference,
layout_cache_prop_v: &NamedReference,
diag: &mut BuildDiagnostics,
) {
let mut get_const_value = |name: &str| {
item_element
.borrow_mut()
.bindings
.remove(name)
.and_then(|e| eval_const_expr(&e.borrow().expression, name, &*e.borrow(), diag))
};
let colspan = get_const_value("colspan").unwrap_or(1);
let rowspan = get_const_value("rowspan").unwrap_or(1);
if let Some(r) = get_const_value("row") {
*row = r;
*col = 0;
}
if let Some(c) = get_const_value("col") {
*col = c;
}
self.add_element_with_coord(
item_element,
(*row, *col),
(rowspan, colspan),
layout_cache_prop_h,
layout_cache_prop_v,
diag,
)
}
fn add_element_with_coord(
&mut self,
item_element: &ElementRc,
(row, col): (u16, u16),
(rowspan, colspan): (u16, u16),
layout_cache_prop_h: &NamedReference,
layout_cache_prop_v: &NamedReference,
diag: &mut BuildDiagnostics,
) {
let index = self.elems.len();
if let Some(layout_item) = create_layout_item(item_element, diag) {
if layout_item.repeater_index.is_some() {
diag.push_error(
"'if' or 'for' expressions are not currently supported in grid layouts"
.to_string(),
&*item_element.borrow(),
);
return;
}
let e = &layout_item.elem;
set_prop_from_cache(e, "x", layout_cache_prop_h, index * 2, &None, diag);
if !layout_item.item.constraints.fixed_width {
set_prop_from_cache(e, "width", layout_cache_prop_h, index * 2 + 1, &None, diag);
}
set_prop_from_cache(e, "y", layout_cache_prop_v, index * 2, &None, diag);
if !layout_item.item.constraints.fixed_height {
set_prop_from_cache(e, "height", layout_cache_prop_v, index * 2 + 1, &None, diag);
}
self.elems.push(GridLayoutElement {
col,
row,
colspan,
rowspan,
item: layout_item.item,
});
}
}
}
fn lower_box_layout(
layout_element: &ElementRc,
diag: &mut BuildDiagnostics,
orientation: Orientation,
) {
let mut layout = BoxLayout {
orientation,
elems: Default::default(),
geometry: LayoutGeometry::new(layout_element),
};
let layout_cache_prop = create_new_prop(layout_element, "layout-cache", Type::LayoutCache);
let layout_info_prop_v = create_new_prop(layout_element, "layoutinfo-v", layout_info_type());
let layout_info_prop_h = create_new_prop(layout_element, "layoutinfo-h", layout_info_type());
let layout_children = std::mem::take(&mut layout_element.borrow_mut().children);
let (begin_padding, end_padding) = match orientation {
Orientation::Horizontal => (&layout.geometry.padding.top, &layout.geometry.padding.bottom),
Orientation::Vertical => (&layout.geometry.padding.left, &layout.geometry.padding.right),
};
let (pos, size, pad, ortho) = match orientation {
Orientation::Horizontal => ("x", "width", "y", "height"),
Orientation::Vertical => ("y", "height", "x", "width"),
};
let pad_expr = begin_padding.clone().map(Expression::PropertyReference);
let mut size_expr = Expression::PropertyReference(NamedReference::new(layout_element, ortho));
if let Some(p) = begin_padding {
size_expr = Expression::BinaryExpression {
lhs: Box::new(std::mem::take(&mut size_expr)),
rhs: Box::new(Expression::PropertyReference(p.clone())),
op: '-',
}
}
if let Some(p) = end_padding {
size_expr = Expression::BinaryExpression {
lhs: Box::new(std::mem::take(&mut size_expr)),
rhs: Box::new(Expression::PropertyReference(p.clone())),
op: '-',
}
}
for layout_child in &layout_children {
if let Some(item) = create_layout_item(layout_child, diag) {
let index = layout.elems.len() * 2;
let rep_idx = &item.repeater_index;
let (fixed_size, fixed_ortho) = match orientation {
Orientation::Horizontal => {
(item.item.constraints.fixed_width, item.item.constraints.fixed_height)
}
Orientation::Vertical => {
(item.item.constraints.fixed_height, item.item.constraints.fixed_width)
}
};
let actual_elem = &item.elem;
set_prop_from_cache(actual_elem, pos, &layout_cache_prop, index, rep_idx, diag);
if !fixed_size {
set_prop_from_cache(
actual_elem,
size,
&layout_cache_prop,
index + 1,
rep_idx,
diag,
);
}
if let Some(pad_expr) = pad_expr.clone() {
actual_elem.borrow_mut().bindings.insert(pad.into(), RefCell::new(pad_expr.into()));
}
if !fixed_ortho {
actual_elem
.borrow_mut()
.bindings
.insert(ortho.into(), RefCell::new(size_expr.clone().into()));
}
layout.elems.push(item.item);
}
}
layout_element.borrow_mut().children = layout_children;
let span = layout_element.borrow().to_source_location();
layout_cache_prop.element().borrow_mut().bindings.insert(
layout_cache_prop.name().into(),
BindingExpression::new_with_span(
Expression::SolveLayout(Layout::BoxLayout(layout.clone()), orientation),
span.clone(),
)
.into(),
);
layout_info_prop_h.element().borrow_mut().bindings.insert(
layout_info_prop_h.name().into(),
BindingExpression::new_with_span(
Expression::ComputeLayoutInfo(
Layout::BoxLayout(layout.clone()),
Orientation::Horizontal,
),
span.clone(),
)
.into(),
);
layout_info_prop_v.element().borrow_mut().bindings.insert(
layout_info_prop_v.name().into(),
BindingExpression::new_with_span(
Expression::ComputeLayoutInfo(Layout::BoxLayout(layout), Orientation::Vertical),
span,
)
.into(),
);
layout_element.borrow_mut().layout_info_prop = Some((layout_info_prop_h, layout_info_prop_v));
}
fn lower_dialog_layout(
dialog_element: &ElementRc,
style_metrics: &Option<Rc<Component>>,
diag: &mut BuildDiagnostics,
) {
let mut grid = GridLayout {
elems: Default::default(),
geometry: LayoutGeometry::new(dialog_element),
dialog_button_roles: None,
};
if let Some(metrics) = style_metrics.as_ref().map(|comp| &comp.root_element) {
grid.geometry.padding.bottom.get_or_insert(NamedReference::new(metrics, "layout-padding"));
grid.geometry.padding.top.get_or_insert(NamedReference::new(metrics, "layout-padding"));
grid.geometry.padding.left.get_or_insert(NamedReference::new(metrics, "layout-padding"));
grid.geometry.padding.right.get_or_insert(NamedReference::new(metrics, "layout-padding"));
grid.geometry.spacing.get_or_insert(NamedReference::new(metrics, "layout-spacing"));
}
let layout_cache_prop_h = create_new_prop(dialog_element, "layout-cache-h", Type::LayoutCache);
let layout_cache_prop_v = create_new_prop(dialog_element, "layout-cache-v", Type::LayoutCache);
let layout_info_prop_h = create_new_prop(dialog_element, "layoutinfo-h", layout_info_type());
let layout_info_prop_v = create_new_prop(dialog_element, "layoutinfo-v", layout_info_type());
let mut main_widget = None;
let mut button_roles = vec![];
let mut seen_buttons = HashSet::new();
let layout_children = std::mem::take(&mut dialog_element.borrow_mut().children);
for layout_child in &layout_children {
let dialog_button_role_binding =
layout_child.borrow_mut().bindings.remove("dialog-button-role");
let is_button = if let Some(role_binding) = dialog_button_role_binding {
let role_binding = role_binding.into_inner();
if let Expression::EnumerationValue(val) = &role_binding.expression {
let en = &val.enumeration;
debug_assert_eq!(en.name, "DialogButtonRole");
button_roles.push(en.values[val.value].clone());
if val.value == 0 {
diag.push_error(
"The `dialog-button-role` cannot be set explicitly to none".into(),
&role_binding,
);
}
} else {
diag.push_error(
"The `dialog-button-role` property must be known at compile-time".into(),
&role_binding,
);
}
true
} else if matches!(&layout_child.borrow().lookup_property("kind").property_type, Type::Enumeration(e) if e.name == "StandardButtonKind")
{
// layout_child is a StandardButton
match layout_child.borrow().bindings.get("kind") {
None => diag.push_error(
"The `kind` property of the StandardButton in a Dialog must be set".into(),
&*layout_child.borrow(),
),
Some(binding) => {
let binding = &*binding.borrow();
if let Expression::EnumerationValue(val) = &binding.expression {
let en = &val.enumeration;
debug_assert_eq!(en.name, "StandardButtonKind");
let kind = &en.values[val.value];
let role = match kind.as_str() {
"ok" => "accept",
"cancel" => "reject",
"apply" => "apply",
"close" => "reject",
"reset" => "reset",
"help" => "help",
"yes" => "accept",
"no" => "reject",
"abort" => "reject",
"retry" => "accept",
"ignore" => "accept",
_ => unreachable!(),
};
button_roles.push(role.into());
if !seen_buttons.insert(val.value) {
diag.push_error("Duplicated `kind`: There are two StandardButton in this Dialog with the same kind".into(), binding);
} else if Rc::ptr_eq(
dialog_element,
&dialog_element
.borrow()
.enclosing_component
.upgrade()
.unwrap()
.root_element,
) {
let clicked_ty =
layout_child.borrow().lookup_property("clicked").property_type;
if matches!(&clicked_ty, Type::Callback { .. })
&& layout_child.borrow().bindings.get("clicked").map_or(true, |c| {
matches!(c.borrow().expression, Expression::Invalid)
})
{
dialog_element
.borrow_mut()
.property_declarations
.entry(format!("{}-clicked", kind))
.or_insert_with(|| PropertyDeclaration {
property_type: clicked_ty,
node: None,
expose_in_public_api: true,
is_alias: Some(NamedReference::new(
layout_child,
"clicked",
)),
});
}
}
} else {
diag.push_error(
"The `kind` property of the StandardButton in a Dialog must be known at compile-time"
.into(),
binding,
);
}
}
}
true
} else {
false
};
if is_button {
grid.add_element_with_coord(
layout_child,
(1, button_roles.len() as u16),
(1, 1),
&layout_cache_prop_h,
&layout_cache_prop_v,
diag,
);
} else if main_widget.is_some() {
diag.push_error(
"A Dialog can have only one child element that is not a StandardButton".into(),
&*layout_child.borrow(),
);
} else {
main_widget = Some(layout_child.clone())
}
}
dialog_element.borrow_mut().children = layout_children;
if let Some(main_widget) = main_widget {
grid.add_element_with_coord(
&main_widget,
(0, 0),
(1, button_roles.len() as u16 + 1),
&layout_cache_prop_h,
&layout_cache_prop_v,
diag,
);
} else {
diag.push_error(
"A Dialog must have a single child element that is not StandardButton".into(),
&*dialog_element.borrow(),
);
}
grid.dialog_button_roles = Some(button_roles);
let span = dialog_element.borrow().to_source_location();
layout_cache_prop_h.element().borrow_mut().bindings.insert(
layout_cache_prop_h.name().into(),
BindingExpression::new_with_span(
Expression::SolveLayout(Layout::GridLayout(grid.clone()), Orientation::Horizontal),
span.clone(),
)
.into(),
);
layout_cache_prop_v.element().borrow_mut().bindings.insert(
layout_cache_prop_v.name().into(),
BindingExpression::new_with_span(
Expression::SolveLayout(Layout::GridLayout(grid.clone()), Orientation::Vertical),
span.clone(),
)
.into(),
);
layout_info_prop_h.element().borrow_mut().bindings.insert(
layout_info_prop_h.name().into(),
BindingExpression::new_with_span(
Expression::ComputeLayoutInfo(
Layout::GridLayout(grid.clone()),
Orientation::Horizontal,
),
span.clone(),
)
.into(),
);
layout_info_prop_v.element().borrow_mut().bindings.insert(
layout_info_prop_v.name().into(),
BindingExpression::new_with_span(
Expression::ComputeLayoutInfo(Layout::GridLayout(grid), Orientation::Vertical),
span,
)
.into(),
);
dialog_element.borrow_mut().layout_info_prop = Some((layout_info_prop_h, layout_info_prop_v));
}
fn lower_path_layout(layout_element: &ElementRc, diag: &mut BuildDiagnostics) {
let layout_cache_prop = create_new_prop(layout_element, "layout-cache", Type::LayoutCache);
let path_elements_expr = match layout_element
.borrow_mut()
.bindings
.remove("elements")
.map(RefCell::into_inner)
{
Some(BindingExpression { expression: Expression::PathData(data), .. }) => data,
_ => {
diag.push_error("Internal error: elements binding in PathLayout does not contain path elements expression".into(), &*layout_element.borrow());
return;
}
};
let elements = layout_element.borrow().children.clone();
if elements.is_empty() {
return;
}
for (index, e) in elements.iter().enumerate() {
let (repeater_index, actual_elem) = if e.borrow().repeated.is_some() {
(
Some(Expression::RepeaterIndexReference { element: Rc::downgrade(e) }),
e.borrow().base_type.as_component().root_element.clone(),
)
} else {
(None, e.clone())
};
let set_prop_from_cache = |prop: &str, offset: usize, size_prop: &str| {
let size = NamedReference::new(&actual_elem, size_prop);
let expression = Expression::BinaryExpression {
lhs: Box::new(Expression::LayoutCacheAccess {
layout_cache_prop: layout_cache_prop.clone(),
index: index * 2 + offset,
repeater_index: repeater_index.as_ref().map(|x| Box::new(x.clone())),
}),
op: '-',
rhs: Box::new(Expression::BinaryExpression {
lhs: Box::new(Expression::PropertyReference(size)),
op: '/',
rhs: Box::new(Expression::NumberLiteral(2., Unit::None)),
}),
};
actual_elem.borrow_mut().bindings.insert(prop.into(), RefCell::new(expression.into()));
};
set_prop_from_cache("x", 0, "width");
set_prop_from_cache("y", 1, "height");
}
let rect = LayoutRect::install_on_element(layout_element);
let path_layout = Layout::PathLayout(PathLayout {
elements,
path: path_elements_expr,
rect,
offset_reference: layout_element
.borrow()
.bindings
.contains_key("spacing")
.then(|| NamedReference::new(layout_element, "spacing")),
});
let binding = BindingExpression::new_with_span(
Expression::SolveLayout(path_layout, Orientation::Horizontal),
layout_element.borrow().to_source_location(),
);
layout_cache_prop
.element()
.borrow_mut()
.bindings
.insert(layout_cache_prop.name().into(), binding.into());
}
struct CreateLayoutItemResult {
item: LayoutItem,
elem: ElementRc,
repeater_index: Option<Expression>,
}
/// Create a LayoutItem for the given `item_element` returns None is the layout is empty
fn create_layout_item(
item_element: &ElementRc,
diag: &mut BuildDiagnostics,
) -> Option<CreateLayoutItemResult> {
let fix_explicit_percent = |prop: &str, item: &ElementRc| {
if !item.borrow().bindings.get(prop).map_or(false, |b| b.borrow().ty() == Type::Percent) {
return;
}
let mut item = item.borrow_mut();
let b = item.bindings.remove(prop).unwrap();
item.bindings.insert(format!("min-{}", prop), b.clone());
item.bindings.insert(format!("max-{}", prop), b);
item.property_declarations.insert(
format!("min-{}", prop),
PropertyDeclaration { property_type: Type::Percent, ..PropertyDeclaration::default() },
);
item.property_declarations.insert(
format!("max-{}", prop),
PropertyDeclaration { property_type: Type::Percent, ..PropertyDeclaration::default() },
);
};
fix_explicit_percent("width", item_element);
fix_explicit_percent("height", item_element);
item_element.borrow_mut().child_of_layout = true;
let (repeater_index, actual_elem) = if let Some(r) = &item_element.borrow().repeated {
let rep_comp = item_element.borrow().base_type.as_component().clone();
fix_explicit_percent("width", &rep_comp.root_element);
fix_explicit_percent("height", &rep_comp.root_element);
*rep_comp.root_constraints.borrow_mut() =
LayoutConstraints::new(&rep_comp.root_element, diag);
rep_comp.root_element.borrow_mut().child_of_layout = true;
(
Some(if r.is_conditional_element {
Expression::NumberLiteral(0., Unit::None)
} else {
Expression::RepeaterIndexReference { element: Rc::downgrade(item_element) }
}),
rep_comp.root_element.clone(),
)
} else {
(None, item_element.clone())
};
let constraints = LayoutConstraints::new(&actual_elem, diag);
Some(CreateLayoutItemResult {
item: LayoutItem { element: item_element.clone(), constraints },
elem: actual_elem,
repeater_index,
})
}
fn set_prop_from_cache(
elem: &ElementRc,
prop: &str,
layout_cache_prop: &NamedReference,
index: usize,
repeater_index: &Option<Expression>,
diag: &mut BuildDiagnostics,
) {
let old = elem.borrow_mut().bindings.insert(
prop.into(),
BindingExpression::new_with_span(
Expression::LayoutCacheAccess {
layout_cache_prop: layout_cache_prop.clone(),
index,
repeater_index: repeater_index.as_ref().map(|x| Box::new(x.clone())),
},
layout_cache_prop.element().borrow().to_source_location(),
)
.into(),
);
if let Some(old) = old.map(RefCell::into_inner) {
diag.push_error(
format!("The property '{}' cannot be set for elements placed in a layout, because the layout is already setting it", prop),
&old,
);
}
}
fn eval_const_expr(
expression: &Expression,
name: &str,
span: &dyn crate::diagnostics::Spanned,
diag: &mut BuildDiagnostics,
) -> Option<u16> {
match expression {
Expression::NumberLiteral(v, Unit::None) => {
if *v < 0. || *v > u16::MAX as f64 || !v.trunc().approx_eq(v) {
diag.push_error(format!("'{}' must be a positive integer", name), span);
None
} else {
Some(*v as u16)
}
}
Expression::Cast { from, .. } => eval_const_expr(from, name, span, diag),
_ => {
diag.push_error(format!("'{}' must be an integer literal", name), span);
None
}
}
}
/// Create a new property based on the name. (it might get a different name if that property exist)
pub fn create_new_prop(elem: &ElementRc, tentative_name: &str, ty: Type) -> NamedReference {
let mut e = elem.borrow_mut();
if !e.lookup_property(tentative_name).is_valid() {
e.property_declarations.insert(tentative_name.into(), ty.into());
drop(e);
NamedReference::new(elem, tentative_name)
} else {
let mut counter = 0;
loop {
counter += 1;
let name = format!("{}{}", tentative_name, counter);
if !e.lookup_property(&name).is_valid() {
e.property_declarations.insert(name.clone(), ty.into());
drop(e);
return NamedReference::new(elem, &name);
}
}
}
}
/// Checks that there is grid-layout specific properties left
fn check_no_layout_properties(item: &ElementRc, diag: &mut BuildDiagnostics) {
for (prop, expr) in item.borrow().bindings.iter() {
if matches!(prop.as_ref(), "col" | "row" | "colspan" | "rowspan") {
diag.push_error(format!("{} used outside of a GridLayout", prop), &*expr.borrow());
}
if matches!(prop.as_ref(), "dialog-button-role") {
diag.push_error(format!("{} used outside of a Dialog", prop), &*expr.borrow());
}
}
}