Delay the percentage size conversion to after the layouting phase

So we can see inthe layouting phase if the size was in percent
This commit is contained in:
Olivier Goffart 2021-02-11 14:59:49 +01:00
parent 92218a8b6e
commit 0174db3679
6 changed files with 67 additions and 53 deletions

View file

@ -628,6 +628,7 @@ fn test_select_minimal_class_based_on_property_usage() {
assert_eq!(reduce_to_second.class_name, second.class_name);
}
#[derive(PartialEq, Debug)]
pub struct PropertyLookupResult<'a> {
pub resolved_name: std::borrow::Cow<'a, str>,
pub property_type: Type,

View file

@ -182,7 +182,7 @@ pub async fn run_passes(
passes::lower_popups::lower_popups(&doc.root_component, &doc.local_registry, diag);
passes::lower_layout::lower_layouts(&doc.root_component, &mut type_loader, diag).await;
passes::lower_shadows::lower_shadow_properties(&doc.root_component, &doc.local_registry, diag);
passes::default_geometry::default_geometry(&doc.root_component);
passes::default_geometry::default_geometry(&doc.root_component, diag);
passes::apply_default_properties_from_style::apply_default_properties_from_style(
&doc.root_component,
&mut type_loader,

View file

@ -12,6 +12,7 @@ LICENSE END */
use std::rc::Rc;
use crate::diagnostics::BuildDiagnostics;
use crate::langtype::DefaultSizeBinding;
use crate::langtype::Type;
use crate::object_tree::{Component, ElementRc};
@ -20,11 +21,14 @@ use crate::{
langtype::PropertyLookupResult,
};
pub fn default_geometry(root_component: &Rc<Component>) {
pub fn default_geometry(root_component: &Rc<Component>, diag: &mut BuildDiagnostics) {
crate::object_tree::recurse_elem_including_sub_components(
&root_component,
&None,
&mut |elem: &ElementRc, parent: &Option<ElementRc>| {
fix_percent_size(elem, parent, "width", diag);
fix_percent_size(elem, parent, "height", diag);
let base_type = elem.borrow().base_type.clone();
if let (Some(parent), Type::Builtin(builtin_type)) = (parent, base_type) {
match builtin_type.default_size_binding {
@ -46,6 +50,37 @@ pub fn default_geometry(root_component: &Rc<Component>) {
)
}
/// Replace expression such as `"width: 30%;` with `width: 0.3 * parent.width;`
fn fix_percent_size(
elem: &ElementRc,
parent: &Option<ElementRc>,
property: &str,
diag: &mut BuildDiagnostics,
) {
if !elem.borrow().bindings.get(property).map_or(false, |b| b.ty() == Type::Percent) {
return;
}
let mut elem = elem.borrow_mut();
let b = elem.bindings.get_mut(property).unwrap();
if let Some(parent) = parent {
debug_assert_eq!(
parent.borrow().lookup_property(property),
PropertyLookupResult { resolved_name: property.into(), property_type: Type::Length }
);
b.expression = Expression::BinaryExpression {
lhs: Box::new(std::mem::take(&mut b.expression).maybe_convert_to(
Type::Float32,
&b.span,
diag,
)),
rhs: Box::new(Expression::PropertyReference(NamedReference::new(parent, property))),
op: '*',
}
} else {
diag.push_error("Cannot find parent property to apply relative lenght".into(), &b.span);
}
}
fn make_default_100(elem: &ElementRc, parent_element: &ElementRc, property: &str) {
let PropertyLookupResult { resolved_name, property_type } =
parent_element.borrow().lookup_property(property);

View file

@ -212,55 +212,6 @@ fn find_parent_element(e: &ElementRc) -> Option<ElementRc> {
recurse(&root, e)
}
/// If the type of the expression is a percentage, and the current property evaluated is
/// `width` or `height`, attempt to multiply by the parent `width` or `height`
fn attempt_percent_conversion(
ctx: &mut LookupCtx,
e: Expression,
node: &dyn SpannedWithSourceFile,
) -> Expression {
if ctx.property_type != Type::Length || e.ty() != Type::Percent {
return e;
}
const RELATIVE_TO_PARENT_PROPERTIES: [&str; 2] = ["width", "height"];
let property_name = ctx.property_name.unwrap_or_default();
if !RELATIVE_TO_PARENT_PROPERTIES.contains(&property_name) {
ctx.diag.push_error(
format!(
"Automatic conversion from percentage to lenght is only possible for the properties {}",
RELATIVE_TO_PARENT_PROPERTIES.join(" and ")
),
node
);
return Expression::Invalid;
}
let mut parent = ctx.component_scope.last().and_then(find_parent_element);
while let Some(p) = parent {
let PropertyLookupResult { resolved_name, property_type } =
p.borrow().lookup_property(property_name);
if property_type == Type::Length {
return Expression::BinaryExpression {
lhs: Box::new(Expression::BinaryExpression {
lhs: Box::new(e),
rhs: Box::new(Expression::NumberLiteral(0.01, Unit::None)),
op: '*',
}),
rhs: Box::new(Expression::PropertyReference(NamedReference {
element: Rc::downgrade(&p),
name: resolved_name.to_string(),
})),
op: '*',
};
}
parent = find_parent_element(&p);
}
ctx.diag.push_error("Cannot find parent property to apply relative lenght".into(), node);
Expression::Invalid
}
impl Expression {
pub fn from_binding_expression_node(
node: SyntaxNodeWithSourceFile,
@ -275,7 +226,23 @@ impl Expression {
.map(|c| Self::from_codeblock_node(c.into(), ctx))
})
.unwrap_or(Self::Invalid);
let e = attempt_percent_conversion(ctx, e, &node);
if ctx.property_type == Type::Length && e.ty() == Type::Percent {
// See if a conversion from percentage to lenght is allowed
const RELATIVE_TO_PARENT_PROPERTIES: [&str; 2] = ["width", "height"];
let property_name = ctx.property_name.unwrap_or_default();
if RELATIVE_TO_PARENT_PROPERTIES.contains(&property_name) {
return e;
} else {
ctx.diag.push_error(
format!(
"Automatic conversion from percentage to lenght is only possible for the properties {}",
RELATIVE_TO_PARENT_PROPERTIES.join(" and ")
),
&node
);
return Expression::Invalid;
}
};
e.maybe_convert_to(ctx.property_type.clone(), &node, &mut ctx.diag)
}

View file

@ -10,7 +10,6 @@ LICENSE END */
Foo := Rectangle {
width: 30%;
// ^error{Cannot find parent property to apply relative lenght}
Rectangle {
height: 111%;