From 1f091cb1c04c83c8041ae2fe17f4277c7367baf5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Tue, 2 Feb 2021 09:07:01 +0100 Subject: [PATCH] Rename Rectangle.color to Rectangle.background Add support for built-in property aliases and rename `color` to `background` - in preparation for it also changing to type brush. Right now the alias is silent, a deprecation and overall change will come in a subsequent change. --- sixtyfps_compiler/builtins.60 | 3 +- sixtyfps_compiler/expression_tree.rs | 8 +- sixtyfps_compiler/generator/cpp.rs | 6 +- sixtyfps_compiler/generator/rust.rs | 6 +- sixtyfps_compiler/langtype.rs | 61 ++++++-- sixtyfps_compiler/layout.rs | 10 +- sixtyfps_compiler/load_builtins.rs | 29 +++- sixtyfps_compiler/object_tree.rs | 131 +++++++++++------- sixtyfps_compiler/passes/default_geometry.rs | 14 +- sixtyfps_compiler/passes/lower_states.rs | 6 +- sixtyfps_compiler/passes/resolving.rs | 58 ++++---- .../tests/syntax/basic/states_transitions2.60 | 2 +- sixtyfps_runtime/corelib/items.rs | 4 +- .../interpreter/dynamic_component.rs | 35 +++-- sixtyfps_runtime/rendering_backends/gl/lib.rs | 4 +- .../rendering_backends/qt/qt_window.rs | 4 +- 16 files changed, 250 insertions(+), 131 deletions(-) diff --git a/sixtyfps_compiler/builtins.60 b/sixtyfps_compiler/builtins.60 index ad64e0a5a..c3c2cb61b 100644 --- a/sixtyfps_compiler/builtins.60 +++ b/sixtyfps_compiler/builtins.60 @@ -19,7 +19,8 @@ LICENSE END */ */ Rectangle := _ { - property color; + property background; + property color <=> background; property x; property y; property width; diff --git a/sixtyfps_compiler/expression_tree.rs b/sixtyfps_compiler/expression_tree.rs index 655820681..6a96d40c5 100644 --- a/sixtyfps_compiler/expression_tree.rs +++ b/sixtyfps_compiler/expression_tree.rs @@ -420,13 +420,13 @@ impl Expression { Expression::NumberLiteral(_, unit) => unit.ty(), Expression::BoolLiteral(_) => Type::Bool, Expression::TwoWayBinding(NamedReference { element, name }, _) => { - element.upgrade().unwrap().borrow().lookup_property(name) + element.upgrade().unwrap().borrow().lookup_property(name).property_type } Expression::CallbackReference(NamedReference { element, name }) => { - element.upgrade().unwrap().borrow().lookup_property(name) + element.upgrade().unwrap().borrow().lookup_property(name).property_type } Expression::PropertyReference(NamedReference { element, name }) => { - element.upgrade().unwrap().borrow().lookup_property(name) + element.upgrade().unwrap().borrow().lookup_property(name).property_type } Expression::BuiltinFunctionReference(funcref) => funcref.ty(), Expression::MemberFunction { member, .. } => member.ty(), @@ -456,7 +456,7 @@ impl Expression { Type::Object { fields, .. } => { fields.get(name.as_str()).unwrap_or(&Type::Invalid).clone() } - Type::Component(c) => c.root_element.borrow().lookup_property(name.as_str()), + Type::Component(c) => c.root_element.borrow().lookup_property(name).property_type, _ => Type::Invalid, }, Expression::Cast { to, .. } => to.clone(), diff --git a/sixtyfps_compiler/generator/cpp.rs b/sixtyfps_compiler/generator/cpp.rs index 85c585937..1914f1388 100644 --- a/sixtyfps_compiler/generator/cpp.rs +++ b/sixtyfps_compiler/generator/cpp.rs @@ -337,7 +337,7 @@ fn handle_property_binding( let item = elem.borrow(); let component = item.enclosing_component.upgrade().unwrap(); let id = &item.id; - let prop_type = item.lookup_property(prop_name); + let prop_type = item.lookup_property(prop_name).property_type; if let Type::Callback { args, .. } = &prop_type { let callback_accessor_prefix = if item.property_declarations.contains_key(prop_name) { String::new() @@ -2059,14 +2059,14 @@ impl<'a> LayoutTreeItem<'a> { let path_layout_item_data = |elem: &ElementRc, elem_cpp: &str, component_cpp: &str| { let prop_ref = |n: &str| { - if elem.borrow().lookup_property(n) == Type::Length { + if elem.borrow().lookup_property(n).property_type == Type::Length { format!("&{}.{}", elem_cpp, n) } else { "nullptr".to_owned() } }; let prop_value = |n: &str| { - if elem.borrow().lookup_property(n) == Type::Length { + if elem.borrow().lookup_property(n).property_type == Type::Length { let value_accessor = access_member( &elem, n, diff --git a/sixtyfps_compiler/generator/rust.rs b/sixtyfps_compiler/generator/rust.rs index a58289e9b..9e725a399 100644 --- a/sixtyfps_compiler/generator/rust.rs +++ b/sixtyfps_compiler/generator/rust.rs @@ -146,7 +146,7 @@ fn handle_property_binding( init: &mut Vec, ) { let rust_property = access_member(item_rc, prop_name, component, quote!(_self), false); - let prop_type = item_rc.borrow().lookup_property(prop_name); + let prop_type = item_rc.borrow().lookup_property(prop_name).property_type; if matches!(prop_type, Type::Callback{..}) { let tokens_for_expression = compile_expression(binding_expression, &component); init.push(quote!( @@ -1774,7 +1774,7 @@ impl<'a> LayoutTreeItem<'a> { let path_layout_item_data = |elem: &ElementRc, elem_rs: TokenStream, component_rust: TokenStream| { let prop_ref = |n: &str| { - if elem.borrow().lookup_property(n) == Type::Length { + if elem.borrow().lookup_property(n).property_type == Type::Length { let n = format_ident!("{}", n); quote! {Some(& #elem_rs.#n)} } else { @@ -1782,7 +1782,7 @@ impl<'a> LayoutTreeItem<'a> { } }; let prop_value = |n: &str| { - if elem.borrow().lookup_property(n) == Type::Length { + if elem.borrow().lookup_property(n).property_type == Type::Length { let accessor = access_member( &elem, n, diff --git a/sixtyfps_compiler/langtype.rs b/sixtyfps_compiler/langtype.rs index cb8824a96..e288a5f97 100644 --- a/sixtyfps_compiler/langtype.rs +++ b/sixtyfps_compiler/langtype.rs @@ -7,8 +7,10 @@ This file is also available under commercial licensing terms. Please contact info@sixtyfps.io for more information. LICENSE END */ +use std::borrow::Cow; use std::collections::{BTreeMap, HashMap, HashSet}; -use std::{fmt::Display, rc::Rc}; +use std::fmt::Display; +use std::rc::Rc; use crate::expression_tree::{Expression, Unit}; use crate::object_tree::Component; @@ -193,18 +195,39 @@ impl Type { !matches!(self, Self::Duration | Self::Easing | Self::Angle) } - pub fn lookup_property(&self, name: &str) -> Type { + pub fn lookup_property<'a>(&self, name: &'a str) -> PropertyLookupResult<'a> { match self { Type::Component(c) => c.root_element.borrow().lookup_property(name), - Type::Builtin(b) => b.properties.get(name).cloned().unwrap_or_else(|| { - if b.is_non_item_type { - Type::Invalid + Type::Builtin(b) => { + let resolved_name = + if let Some(alias_name) = b.native_class.lookup_alias(name.as_ref()) { + Cow::Owned(alias_name.to_string()) + } else { + Cow::Borrowed(name) + }; + let property_type = + b.properties.get(resolved_name.as_ref()).cloned().unwrap_or_else(|| { + if b.is_non_item_type { + Type::Invalid + } else { + crate::typeregister::reserved_property(resolved_name.as_ref()) + } + }); + PropertyLookupResult { resolved_name, property_type } + } + Type::Native(n) => { + let resolved_name = if let Some(alias_name) = n.lookup_alias(name.as_ref()) { + Cow::Owned(alias_name.to_string()) } else { - crate::typeregister::reserved_property(name) - } - }), - Type::Native(n) => n.lookup_property(name).unwrap_or_default(), - _ => Type::Invalid, + Cow::Borrowed(name) + }; + let property_type = n.lookup_property(resolved_name.as_ref()).unwrap_or_default(); + PropertyLookupResult { resolved_name, property_type } + } + _ => PropertyLookupResult { + resolved_name: Cow::Borrowed(name), + property_type: Type::Invalid, + }, } } @@ -392,6 +415,7 @@ pub struct NativeClass { pub class_name: String, pub vtable_symbol: String, pub properties: HashMap, + pub deprecated_aliases: HashMap, pub cpp_type: Option, pub rust_type_constructor: Option, } @@ -441,6 +465,18 @@ impl NativeClass { } } + pub fn lookup_alias(&self, name: &str) -> Option<&str> { + if let Some(alias_target) = self.deprecated_aliases.get(name) { + Some(alias_target) + } else if self.properties.contains_key(name) { + None + } else if let Some(parent_class) = &self.parent { + parent_class.lookup_alias(name) + } else { + None + } + } + fn lookup_property_distance(self: Rc, name: &str) -> (usize, Rc) { let mut distance = 0; let mut class = self; @@ -559,6 +595,11 @@ fn test_select_minimal_class_based_on_property_usage() { assert_eq!(reduce_to_second.class_name, second.class_name); } +pub struct PropertyLookupResult<'a> { + pub resolved_name: std::borrow::Cow<'a, str>, + pub property_type: Type, +} + #[derive(Debug, Clone)] pub struct Enumeration { pub name: String, diff --git a/sixtyfps_compiler/layout.rs b/sixtyfps_compiler/layout.rs index 4cc0d6b83..b5bb3affc 100644 --- a/sixtyfps_compiler/layout.rs +++ b/sixtyfps_compiler/layout.rs @@ -9,9 +9,9 @@ LICENSE END */ //! Datastructures used to represent layouts in the compiler -use crate::diagnostics::BuildDiagnostics; use crate::langtype::Type; use crate::object_tree::{ElementRc, PropertyDeclaration}; +use crate::{diagnostics::BuildDiagnostics, langtype::PropertyLookupResult}; use crate::{ expression_tree::{Expression, NamedReference, Path}, object_tree::Component, @@ -69,9 +69,11 @@ pub struct LayoutItem { impl LayoutItem { pub fn rect(&self) -> Cow { if let Some(e) = &self.element { - let p = |name: &str| { - if e.borrow().lookup_property(name) == Type::Length { - Some(NamedReference::new(e, name)) + let p = |unresolved_name: &str| { + let PropertyLookupResult { resolved_name, property_type } = + e.borrow().lookup_property(unresolved_name); + if property_type == Type::Length { + Some(NamedReference::new(e, resolved_name.as_ref())) } else { None } diff --git a/sixtyfps_compiler/load_builtins.rs b/sixtyfps_compiler/load_builtins.rs index 39be1492f..a6996b717 100644 --- a/sixtyfps_compiler/load_builtins.rs +++ b/sixtyfps_compiler/load_builtins.rs @@ -81,11 +81,15 @@ pub fn load_builtins(register: &mut TypeRegister) { let mut n = NativeClass::new_with_properties( &id, e.PropertyDeclaration() - .map(|p| { - ( - identifier_text(&p.DeclaredIdentifier()).unwrap(), - object_tree::type_from_node(p.Type(), *diag.borrow_mut(), register), - ) + .flat_map(|p| { + if p.TwoWayBinding().is_some() { + None // aliases are handled further down + } else { + Some(( + identifier_text(&p.DeclaredIdentifier()).unwrap(), + object_tree::type_from_node(p.Type(), *diag.borrow_mut(), register), + )) + } }) .chain(e.CallbackDeclaration().map(|s| { ( @@ -108,6 +112,21 @@ pub fn load_builtins(register: &mut TypeRegister) { ) })), ); + n.deprecated_aliases = e + .PropertyDeclaration() + .flat_map(|p| { + if let Some(twb) = p.TwoWayBinding() { + let alias_name = identifier_text(&p.DeclaredIdentifier()).unwrap(); + let alias_target = identifier_text(&twb.Expression().QualifiedName().expect( + "internal error: built-in aliases can only be declared within the type", + )) + .unwrap(); + Some((alias_name, alias_target)) + } else { + None + } + }) + .collect(); n.cpp_type = parse_annotation("cpp_type", &e.node).map(|x| x.unwrap()); n.rust_type_constructor = parse_annotation("rust_type_constructor", &e.node).map(|x| x.unwrap()); diff --git a/sixtyfps_compiler/object_tree.rs b/sixtyfps_compiler/object_tree.rs index 2aa0310a6..c4dfad16d 100644 --- a/sixtyfps_compiler/object_tree.rs +++ b/sixtyfps_compiler/object_tree.rs @@ -14,12 +14,13 @@ LICENSE END */ use crate::diagnostics::{FileDiagnostics, Spanned, SpannedWithSourceFile}; use crate::expression_tree::{self, Unit}; use crate::expression_tree::{Expression, ExpressionSpanned, NamedReference}; +use crate::langtype::PropertyLookupResult; use crate::langtype::{NativeClass, Type}; use crate::parser::{identifier_text, syntax_nodes, SyntaxKind, SyntaxNodeWithSourceFile}; use crate::typeregister::TypeRegister; -use std::cell::RefCell; use std::collections::HashMap; use std::rc::{Rc, Weak}; +use std::{borrow::Cow, cell::RefCell}; /// The full document (a complete file) #[derive(Default, Debug)] @@ -418,9 +419,13 @@ impl Element { for prop_decl in node.PropertyDeclaration() { let type_node = prop_decl.Type(); let prop_type = type_from_node(type_node.clone(), diag, tr); - let prop_name = identifier_text(&prop_decl.DeclaredIdentifier()).unwrap(); + let unresolved_prop_name = identifier_text(&prop_decl.DeclaredIdentifier()).unwrap(); + let PropertyLookupResult { + resolved_name: prop_name, + property_type: maybe_existing_prop_type, + } = r.lookup_property(&unresolved_prop_name); - if !matches!(r.lookup_property(&prop_name), Type::Invalid) { + if !matches!(maybe_existing_prop_type, Type::Invalid) { diag.push_error( format!("Cannot override property '{}'", prop_name), &prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(), @@ -435,7 +440,7 @@ impl Element { } r.property_declarations.insert( - prop_name.clone(), + prop_name.to_string(), PropertyDeclaration { property_type: prop_type, type_node: Some(type_node.into()), @@ -445,7 +450,7 @@ impl Element { if let Some(csn) = prop_decl.BindingExpression() { if r.bindings - .insert(prop_name.clone(), ExpressionSpanned::new_uncompiled(csn.into())) + .insert(prop_name.to_string(), ExpressionSpanned::new_uncompiled(csn.into())) .is_some() { diag.push_error( @@ -456,7 +461,7 @@ impl Element { } if let Some(csn) = prop_decl.TwoWayBinding() { if r.bindings - .insert(prop_name, ExpressionSpanned::new_uncompiled(csn.into())) + .insert(prop_name.into(), ExpressionSpanned::new_uncompiled(csn.into())) .is_some() { diag.push_error( @@ -504,18 +509,19 @@ impl Element { } for con_node in node.CallbackConnection() { - let name = match identifier_text(&con_node) { + let unresolved_name = match identifier_text(&con_node) { Some(x) => x, None => continue, }; - let prop_type = r.lookup_property(&name); - if let Type::Callback { args, .. } = prop_type { + let PropertyLookupResult { resolved_name, property_type } = + r.lookup_property(&unresolved_name); + if let Type::Callback { args, .. } = property_type { let num_arg = con_node.DeclaredIdentifier().count(); if num_arg > args.len() { diag.push_error( format!( "'{}' only has {} arguments, but {} were provided", - name, + unresolved_name, args.len(), num_arg ), @@ -523,7 +529,10 @@ impl Element { ); } if r.bindings - .insert(name, ExpressionSpanned::new_uncompiled(con_node.clone().into())) + .insert( + resolved_name.into_owned(), + ExpressionSpanned::new_uncompiled(con_node.clone().into()), + ) .is_some() { diag.push_error( @@ -533,7 +542,7 @@ impl Element { } } else { diag.push_error( - format!("'{}' is not a callback{}", name, name_for_looup_errors), + format!("'{}' is not a callback{}", unresolved_name, name_for_looup_errors), &con_node.child_token(SyntaxKind::Identifier).unwrap(), ); } @@ -548,17 +557,21 @@ impl Element { }; for prop_name_token in anim.QualifiedName() { match QualifiedTypeName::from_node(prop_name_token.clone()).members.as_slice() { - [prop_name] => { - let prop_type = r.lookup_property(&prop_name); + [unresolved_prop_name] => { + let PropertyLookupResult { resolved_name, property_type } = + r.lookup_property(&unresolved_prop_name); if let Some(anim_element) = animation_element_from_node( &anim, &prop_name_token, - prop_type, + property_type, diag, tr, ) { if r.property_animations - .insert(prop_name.clone(), PropertyAnimation::Static(anim_element)) + .insert( + resolved_name.to_string(), + PropertyAnimation::Static(anim_element), + ) .is_some() { diag.push_error("Duplicated animation".into(), &prop_name_token) @@ -750,13 +763,14 @@ impl Element { e } - /// Return the type of a property in this element or its base - pub fn lookup_property(&self, name: &str) -> Type { - self.property_declarations - .get(name) - .cloned() - .map(|decl| decl.property_type) - .unwrap_or_else(|| self.base_type.lookup_property(name)) + /// Return the type of a property in this element or its base, along with the final name, in case + /// the provided name points towards a property alias. Type::Invalid is returned if the property does + /// not exist. + pub fn lookup_property<'a>(&self, name: &'a str) -> PropertyLookupResult<'a> { + self.property_declarations.get(name).cloned().map(|decl| decl.property_type).map_or_else( + || self.base_type.lookup_property(name), + |property_type| PropertyLookupResult { resolved_name: name.into(), property_type }, + ) } /// Return the Span of this element in the AST for error reporting @@ -773,26 +787,31 @@ impl Element { diag: &mut FileDiagnostics, ) { for (name_token, b) in bindings { - let name = crate::parser::normalize_identifier(name_token.text()); - let prop_type = self.lookup_property(&name); - if !prop_type.is_property_type() { + let unresolved_name = crate::parser::normalize_identifier(name_token.text()); + let PropertyLookupResult { resolved_name, property_type } = + self.lookup_property(&unresolved_name); + if !property_type.is_property_type() { diag.push_error( - match prop_type { + match property_type { Type::Invalid => { - format!("Unknown property {}{}", name, name_for_lookup_error) + format!("Unknown property {}{}", unresolved_name, name_for_lookup_error) } Type::Callback { .. } => { - format!("'{}' is a callback. Use `=>` to connect", name) + format!("'{}' is a callback. Use `=>` to connect", unresolved_name) } _ => format!( "Cannot assign to {}{} because it does not have a valid property type.", - name, name_for_lookup_error + unresolved_name, name_for_lookup_error ), }, &name_token, ); } - if self.bindings.insert(name, ExpressionSpanned::new_uncompiled(b)).is_some() { + if self + .bindings + .insert(resolved_name.to_string(), ExpressionSpanned::new_uncompiled(b)) + .is_some() + { diag.push_error("Duplicated property binding".into(), &name_token); } } @@ -917,25 +936,34 @@ fn lookup_property_from_qualified_name( ) -> (NamedReference, Type) { let qualname = QualifiedTypeName::from_node(node.clone()); match qualname.members.as_slice() { - [prop_name] => { - let ty = r.borrow().lookup_property(prop_name.as_ref()); - if !ty.is_property_type() { + [unresolved_prop_name] => { + let PropertyLookupResult { resolved_name, property_type } = + r.borrow().lookup_property(unresolved_prop_name.as_ref()); + if !property_type.is_property_type() { diag.push_error(format!("'{}' is not a valid property", qualname), &node); } - (NamedReference { element: Rc::downgrade(&r), name: prop_name.clone() }, ty) + ( + NamedReference { element: Rc::downgrade(&r), name: resolved_name.to_string() }, + property_type, + ) } - [elem_id, prop_name] => { - let (element, ty) = if let Some(element) = find_element_by_id(&r, elem_id.as_ref()) { - let ty = element.borrow().lookup_property(prop_name.as_ref()); - if !ty.is_property_type() { - diag.push_error(format!("'{}' not found in '{}'", prop_name, elem_id), &node); - } - (Rc::downgrade(&element), ty) - } else { - diag.push_error(format!("'{}' is not a valid element id", elem_id), &node); - (Weak::new(), Type::Invalid) - }; - (NamedReference { element, name: prop_name.clone() }, ty) + [elem_id, unresolved_prop_name] => { + let (element, name, ty) = + if let Some(element) = find_element_by_id(&r, elem_id.as_ref()) { + let PropertyLookupResult { resolved_name, property_type } = + element.borrow().lookup_property(unresolved_prop_name.as_ref()); + if !property_type.is_property_type() { + diag.push_error( + format!("'{}' not found in '{}'", unresolved_prop_name, elem_id), + &node, + ); + } + (Rc::downgrade(&element), resolved_name, property_type) + } else { + diag.push_error(format!("'{}' is not a valid element id", elem_id), &node); + (Weak::new(), Cow::Borrowed(unresolved_prop_name.as_ref()), Type::Invalid) + }; + (NamedReference { element, name: name.to_string() }, ty) } _ => { diag.push_error(format!("'{}' is not a valid property", qualname), &node); @@ -1049,7 +1077,7 @@ pub fn visit_element_expressions( ) { let mut bindings = std::mem::take(&mut elem.borrow_mut().bindings); for (name, expr) in &mut bindings { - vis(expr, Some(name.as_str()), &|| elem.borrow().lookup_property(name)); + vis(expr, Some(name.as_str()), &|| elem.borrow().lookup_property(name).property_type); } elem.borrow_mut().bindings = bindings; } @@ -1068,7 +1096,12 @@ pub fn visit_element_expressions( } for (ne, e) in &mut s.property_changes { vis(e, Some(ne.name.as_ref()), &|| { - ne.element.upgrade().unwrap().borrow().lookup_property(ne.name.as_ref()) + ne.element + .upgrade() + .unwrap() + .borrow() + .lookup_property(ne.name.as_ref()) + .property_type }); } } diff --git a/sixtyfps_compiler/passes/default_geometry.rs b/sixtyfps_compiler/passes/default_geometry.rs index 2ed9bdb65..3c91febbc 100644 --- a/sixtyfps_compiler/passes/default_geometry.rs +++ b/sixtyfps_compiler/passes/default_geometry.rs @@ -12,10 +12,13 @@ LICENSE END */ use std::rc::Rc; -use crate::expression_tree::{BuiltinFunction, Expression, NamedReference}; use crate::langtype::DefaultSizeBinding; use crate::langtype::Type; use crate::object_tree::{Component, ElementRc}; +use crate::{ + expression_tree::{BuiltinFunction, Expression, NamedReference}, + langtype::PropertyLookupResult, +}; pub fn default_geometry(root_component: &Rc) { crate::object_tree::recurse_elem_including_sub_components( @@ -44,11 +47,14 @@ pub fn default_geometry(root_component: &Rc) { } fn make_default_100(elem: &ElementRc, parent_element: &ElementRc, property: &str) { - if parent_element.borrow().lookup_property(property) != Type::Length { + let PropertyLookupResult { resolved_name, property_type } = + parent_element.borrow().lookup_property(property); + if property_type != Type::Length { return; } - elem.borrow_mut().bindings.entry(property.into()).or_insert_with(|| { - Expression::PropertyReference(NamedReference::new(parent_element, property)).into() + elem.borrow_mut().bindings.entry(resolved_name.to_string()).or_insert_with(|| { + Expression::PropertyReference(NamedReference::new(parent_element, resolved_name.as_ref())) + .into() }); } diff --git a/sixtyfps_compiler/passes/lower_states.rs b/sixtyfps_compiler/passes/lower_states.rs index 512586709..075170667 100644 --- a/sixtyfps_compiler/passes/lower_states.rs +++ b/sixtyfps_compiler/passes/lower_states.rs @@ -149,7 +149,9 @@ fn lower_transitions_in_element( /// Returns a suitable unique name for the "state" property fn compute_state_property_name(root_element: &ElementRc) -> String { let mut property_name = "state".to_owned(); - while root_element.borrow().lookup_property(property_name.as_ref()) != Type::Invalid { + while root_element.borrow().lookup_property(property_name.as_ref()).property_type + != Type::Invalid + { property_name += "_"; } property_name @@ -168,5 +170,5 @@ fn expression_for_property(element: &ElementRc, name: &str) -> Expression { None }; } - Expression::default_value_for_type(&element.borrow().lookup_property(name)) + Expression::default_value_for_type(&element.borrow().lookup_property(name).property_type) } diff --git a/sixtyfps_compiler/passes/resolving.rs b/sixtyfps_compiler/passes/resolving.rs index 12f0656a4..e2a06bea7 100644 --- a/sixtyfps_compiler/passes/resolving.rs +++ b/sixtyfps_compiler/passes/resolving.rs @@ -14,7 +14,6 @@ LICENSE END */ //! //! Most of the code for the resolving actualy lies in the expression_tree module -use crate::diagnostics::{BuildDiagnostics, SpannedWithSourceFile}; use crate::expression_tree::*; use crate::langtype::Type; use crate::object_tree::*; @@ -22,6 +21,10 @@ use crate::parser::{ identifier_text, syntax_nodes, NodeOrTokenWithSourceFile, SyntaxKind, SyntaxNodeWithSourceFile, }; use crate::typeregister::TypeRegister; +use crate::{ + diagnostics::{BuildDiagnostics, SpannedWithSourceFile}, + langtype::PropertyLookupResult, +}; use std::{collections::HashMap, rc::Rc}; /// This represeresent a scope for the Component, where Component is the repeated component, but @@ -235,8 +238,9 @@ fn attempt_percent_conversion( let mut parent = ctx.component_scope.last().and_then(find_parent_element); while let Some(p) = parent { - let ty = p.borrow().lookup_property(property_name); - if ty == Type::Length { + 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), @@ -245,7 +249,7 @@ fn attempt_percent_conversion( }), rhs: Box::new(Expression::PropertyReference(NamedReference { element: Rc::downgrade(&p), - name: property_name.to_string(), + name: resolved_name.to_string(), })), op: '*', }; @@ -577,22 +581,23 @@ impl Expression { } } - let property = elem.borrow().lookup_property(&first_str); - if property.is_property_type() { + let PropertyLookupResult { resolved_name, property_type } = + elem.borrow().lookup_property(&first_str); + if property_type.is_property_type() { let prop = Self::PropertyReference(NamedReference { element: Rc::downgrade(&elem), - name: first_str, + name: resolved_name.to_string(), }); return maybe_lookup_object(prop, it, ctx); - } else if matches!(property, Type::Callback{..}) { + } else if matches!(property_type, Type::Callback{..}) { if let Some(x) = it.next() { ctx.diag.push_error("Cannot access fields of callback".into(), &x) } return Self::CallbackReference(NamedReference { element: Rc::downgrade(&elem), - name: first_str, + name: resolved_name.to_string(), }); - } else if property.is_object_type() { + } else if property_type.is_object_type() { todo!("Continue looking up"); } } @@ -681,8 +686,9 @@ impl Expression { } } - let property = elem.borrow().lookup_property(&first_str); - if property.is_property_type() { + let PropertyLookupResult { resolved_name: _, property_type } = + elem.borrow().lookup_property(&first_str); + if property_type.is_property_type() { report_minus_error(ctx); return Expression::Invalid; } @@ -1128,23 +1134,24 @@ fn continue_lookup_within_element( }; let prop_name = crate::parser::normalize_identifier(second.text()); - let p = elem.borrow().lookup_property(&prop_name); - if p.is_property_type() { + let PropertyLookupResult { resolved_name, property_type } = + elem.borrow().lookup_property(&prop_name); + if property_type.is_property_type() { let prop = Expression::PropertyReference(NamedReference { element: Rc::downgrade(elem), - name: prop_name, + name: resolved_name.to_string(), }); maybe_lookup_object(prop, it, ctx) - } else if matches!(p, Type::Callback{..}) { + } else if matches!(property_type, Type::Callback{..}) { if let Some(x) = it.next() { ctx.diag.push_error("Cannot access fields of callback".into(), &x) } Expression::CallbackReference(NamedReference { element: Rc::downgrade(elem), - name: prop_name, + name: resolved_name.to_string(), }) - } else if matches!(p, Type::Function{..}) { - let member = elem.borrow().base_type.lookup_member_function(&prop_name); + } else if matches!(property_type, Type::Function{..}) { + let member = elem.borrow().base_type.lookup_member_function(&resolved_name); Expression::MemberFunction { base: Box::new(Expression::ElementReference(Rc::downgrade(elem))), base_node: node.into(), @@ -1169,7 +1176,9 @@ fn continue_lookup_within_element( }; if let Some(minus_pos) = second.text().find('-') { // Attempt to recover if the user wanted to write "-" - if elem.borrow().lookup_property(&second.text()[0..minus_pos]) != Type::Invalid { + if elem.borrow().lookup_property(&second.text()[0..minus_pos]).property_type + != Type::Invalid + { err(" Use space before the '-' if you meant a substraction."); return Expression::Invalid; } @@ -1213,15 +1222,16 @@ fn maybe_lookup_object( } } Type::Component(c) => { - let prop_ty = c.root_element.borrow().lookup_property(next_str.as_str()); - if prop_ty != Type::Invalid { + let PropertyLookupResult { resolved_name, property_type } = + c.root_element.borrow().lookup_property(next_str.as_str()); + if property_type != Type::Invalid { base = Expression::ObjectAccess { base: Box::new(std::mem::replace(&mut base, Expression::Invalid)), - name: next.to_string(), + name: resolved_name.to_string(), } } else { return error_or_try_minus(ctx, next, |x| { - c.root_element.borrow().lookup_property(x) != Type::Invalid + c.root_element.borrow().lookup_property(x).property_type != Type::Invalid }); } } diff --git a/sixtyfps_compiler/tests/syntax/basic/states_transitions2.60 b/sixtyfps_compiler/tests/syntax/basic/states_transitions2.60 index 4bc2e094b..652f06830 100644 --- a/sixtyfps_compiler/tests/syntax/basic/states_transitions2.60 +++ b/sixtyfps_compiler/tests/syntax/basic/states_transitions2.60 @@ -24,7 +24,7 @@ TestCase := Rectangle { animate * { } } in checked: { -// ^error{The property 'color' cannot have transition because it already has an animation} +// ^error{The property 'background' cannot have transition because it already has an animation} animate color { } } ] diff --git a/sixtyfps_runtime/corelib/items.rs b/sixtyfps_runtime/corelib/items.rs index ff88771a2..2e0377514 100644 --- a/sixtyfps_runtime/corelib/items.rs +++ b/sixtyfps_runtime/corelib/items.rs @@ -157,7 +157,7 @@ impl ItemWeak { #[pin] /// The implementation of the `Rectangle` element pub struct Rectangle { - pub color: Property, + pub background: Property, pub x: Property, pub y: Property, pub width: Property, @@ -218,7 +218,7 @@ ItemVTable_static! { #[pin] /// The implementation of the `BorderRectangle` element pub struct BorderRectangle { - pub color: Property, + pub background: Property, pub x: Property, pub y: Property, pub width: Property, diff --git a/sixtyfps_runtime/interpreter/dynamic_component.rs b/sixtyfps_runtime/interpreter/dynamic_component.rs index 51fcdad8a..4b43aeabd 100644 --- a/sixtyfps_runtime/interpreter/dynamic_component.rs +++ b/sixtyfps_runtime/interpreter/dynamic_component.rs @@ -14,10 +14,10 @@ use core::ptr::NonNull; use dynamic_type::{Instance, InstanceBox}; use expression_tree::NamedReference; use object_tree::{Element, ElementRc}; -use sixtyfps_compilerlib::expression_tree::Expression; use sixtyfps_compilerlib::langtype::Type; use sixtyfps_compilerlib::layout::{Layout, LayoutConstraints, LayoutItem, PathLayout}; use sixtyfps_compilerlib::*; +use sixtyfps_compilerlib::{expression_tree::Expression, langtype::PropertyLookupResult}; use sixtyfps_corelib::component::{Component, ComponentRefPin, ComponentVTable}; use sixtyfps_corelib::graphics::{Rect, Resource}; use sixtyfps_corelib::item_tree::{ @@ -168,8 +168,10 @@ impl RepeatedComponent for ErasedComponentBox { let root_item = &s.component_type.original.root_element.clone(); let get_prop = |name: &str| { - if root_item.borrow().lookup_property(name) == Type::Length { - let nr = NamedReference::new(root_item, name); + let PropertyLookupResult { resolved_name, property_type } = + root_item.borrow().lookup_property(name); + if property_type == Type::Length { + let nr = NamedReference::new(root_item, resolved_name.as_ref()); let p = get_property_ptr(&nr, s.borrow_instance()); // Safety: assuming get_property_ptr returned a valid pointer, // we know that `Type::Length` is a property of type `f32` @@ -820,9 +822,10 @@ pub fn instantiate<'id>( unsafe { let item = item_within_component.item_from_component(instance_ref.as_ptr()); let elem = item_within_component.elem.borrow(); - for (prop, expr) in &elem.bindings { - let ty = elem.lookup_property(prop.as_str()); - if let Type::Callback { .. } = ty { + for (unresolved_prop_name, expr) in &elem.bindings { + let PropertyLookupResult { resolved_name, property_type } = + elem.lookup_property(unresolved_prop_name.as_str()); + if let Type::Callback { .. } = property_type { let expr = expr.clone(); let component_type = component_type.clone(); let instance = component_box.instance.as_ptr(); @@ -830,7 +833,8 @@ pub fn instantiate<'id>( NonNull::from(&component_type.ct).cast(), instance.cast(), )); - if let Some(callback) = item_within_component.rtti.callbacks.get(prop.as_str()) + if let Some(callback) = + item_within_component.rtti.callbacks.get(resolved_name.as_ref()) { callback.set_handler( item, @@ -845,7 +849,7 @@ pub fn instantiate<'id>( }), ) } else if let Some(callback_offset) = - component_type.custom_callbacks.get(prop.as_str()) + component_type.custom_callbacks.get(resolved_name.as_ref()) { let callback = callback_offset.apply(instance_ref.as_ref()); callback.set_handler(move |args| { @@ -857,13 +861,14 @@ pub fn instantiate<'id>( eval::eval_expression(&expr, &mut local_context) }) } else { - panic!("unkown callback {}", prop) + panic!("unkown callback {}", unresolved_prop_name) } } else { if let Some(prop_rtti) = - item_within_component.rtti.properties.get(prop.as_str()) + item_within_component.rtti.properties.get(resolved_name.as_ref()) { - let maybe_animation = animation_for_property(instance_ref, &elem, prop); + let maybe_animation = + animation_for_property(instance_ref, &elem, resolved_name.as_ref()); let mut e = Some(&expr.expression); while let Some(Expression::TwoWayBinding(nr, next)) = &e { // Safety: The compiler must have ensured that the properties exist and are of the same type @@ -908,14 +913,14 @@ pub fn instantiate<'id>( } } else if let Some(PropertiesWithinComponent { offset, prop: prop_info, .. - }) = component_type.custom_properties.get(prop.as_str()) + }) = component_type.custom_properties.get(resolved_name.as_ref()) { let c = Pin::new_unchecked(vtable::VRef::from_raw( NonNull::from(&component_type.ct).cast(), component_box.instance.as_ptr().cast(), )); - let is_state_info = match ty { + let is_state_info = match property_type { Type::Object { name: Some(name), .. } if name.ends_with("::StateInfo") => { @@ -946,7 +951,7 @@ pub fn instantiate<'id>( let maybe_animation = animation_for_property( instance_ref, &component_type.original.root_element.borrow(), - prop, + resolved_name.as_ref(), ); let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(*offset)); @@ -983,7 +988,7 @@ pub fn instantiate<'id>( } } } else { - panic!("unkown property {}", prop); + panic!("unkown property {}", unresolved_prop_name); } } } diff --git a/sixtyfps_runtime/rendering_backends/gl/lib.rs b/sixtyfps_runtime/rendering_backends/gl/lib.rs index 22ea5746f..f97afbcf3 100644 --- a/sixtyfps_runtime/rendering_backends/gl/lib.rs +++ b/sixtyfps_runtime/rendering_backends/gl/lib.rs @@ -667,7 +667,7 @@ impl ItemRenderer for GLItemRenderer { } // TODO: cache path in item to avoid re-tesselation let mut path = rect_to_path(geometry); - let paint = femtovg::Paint::color(rect.color().into()); + let paint = femtovg::Paint::color(rect.background().into()); self.shared_data.canvas.borrow_mut().save_with(|canvas| { canvas.translate(pos.x, pos.y); canvas.fill_path(&mut path, paint) @@ -705,7 +705,7 @@ impl ItemRenderer for GLItemRenderer { path.rounded_rect(x, y, width, height, border_radius); } - let fill_paint = femtovg::Paint::color(rect.color().into()); + let fill_paint = femtovg::Paint::color(rect.background().into()); let mut border_paint = femtovg::Paint::color(rect.border_color().into()); border_paint.set_line_width(border_width); diff --git a/sixtyfps_runtime/rendering_backends/qt/qt_window.rs b/sixtyfps_runtime/rendering_backends/qt/qt_window.rs index ddb467a64..a55e73774 100644 --- a/sixtyfps_runtime/rendering_backends/qt/qt_window.rs +++ b/sixtyfps_runtime/rendering_backends/qt/qt_window.rs @@ -226,7 +226,7 @@ struct QtItemRenderer<'a> { impl ItemRenderer for QtItemRenderer<'_> { fn draw_rectangle(&mut self, pos: Point, rect: Pin<&items::Rectangle>) { let pos = qttypes::QPoint { x: pos.x as _, y: pos.y as _ }; - let color: u32 = rect.color().as_argb_encoded(); + let color: u32 = rect.background().as_argb_encoded(); let rect: qttypes::QRectF = get_geometry!(pos, items::Rectangle, rect); let painter: &mut QPainter = &mut *self.painter; cpp! { unsafe [painter as "QPainter*", color as "QRgb", rect as "QRectF"] { @@ -237,7 +237,7 @@ impl ItemRenderer for QtItemRenderer<'_> { fn draw_border_rectangle(&mut self, pos: Point, rect: std::pin::Pin<&items::BorderRectangle>) { self.draw_rectangle_impl( get_geometry!(pos, items::BorderRectangle, rect), - rect.color(), + rect.background(), rect.border_color(), rect.border_width(), rect.border_radius(),