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.
This commit is contained in:
Simon Hausmann 2021-02-02 09:07:01 +01:00
parent c479fd132d
commit 1f091cb1c0
16 changed files with 250 additions and 131 deletions

View file

@ -19,7 +19,8 @@ LICENSE END */
*/ */
Rectangle := _ { Rectangle := _ {
property <color> color; property <color> background;
property <color> color <=> background;
property <length> x; property <length> x;
property <length> y; property <length> y;
property <length> width; property <length> width;

View file

@ -420,13 +420,13 @@ impl Expression {
Expression::NumberLiteral(_, unit) => unit.ty(), Expression::NumberLiteral(_, unit) => unit.ty(),
Expression::BoolLiteral(_) => Type::Bool, Expression::BoolLiteral(_) => Type::Bool,
Expression::TwoWayBinding(NamedReference { element, name }, _) => { 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 }) => { 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 }) => { 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::BuiltinFunctionReference(funcref) => funcref.ty(),
Expression::MemberFunction { member, .. } => member.ty(), Expression::MemberFunction { member, .. } => member.ty(),
@ -456,7 +456,7 @@ impl Expression {
Type::Object { fields, .. } => { Type::Object { fields, .. } => {
fields.get(name.as_str()).unwrap_or(&Type::Invalid).clone() 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, _ => Type::Invalid,
}, },
Expression::Cast { to, .. } => to.clone(), Expression::Cast { to, .. } => to.clone(),

View file

@ -337,7 +337,7 @@ fn handle_property_binding(
let item = elem.borrow(); let item = elem.borrow();
let component = item.enclosing_component.upgrade().unwrap(); let component = item.enclosing_component.upgrade().unwrap();
let id = &item.id; 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 { if let Type::Callback { args, .. } = &prop_type {
let callback_accessor_prefix = if item.property_declarations.contains_key(prop_name) { let callback_accessor_prefix = if item.property_declarations.contains_key(prop_name) {
String::new() String::new()
@ -2059,14 +2059,14 @@ impl<'a> LayoutTreeItem<'a> {
let path_layout_item_data = let path_layout_item_data =
|elem: &ElementRc, elem_cpp: &str, component_cpp: &str| { |elem: &ElementRc, elem_cpp: &str, component_cpp: &str| {
let prop_ref = |n: &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) format!("&{}.{}", elem_cpp, n)
} else { } else {
"nullptr".to_owned() "nullptr".to_owned()
} }
}; };
let prop_value = |n: &str| { 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( let value_accessor = access_member(
&elem, &elem,
n, n,

View file

@ -146,7 +146,7 @@ fn handle_property_binding(
init: &mut Vec<TokenStream>, init: &mut Vec<TokenStream>,
) { ) {
let rust_property = access_member(item_rc, prop_name, component, quote!(_self), false); 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{..}) { if matches!(prop_type, Type::Callback{..}) {
let tokens_for_expression = compile_expression(binding_expression, &component); let tokens_for_expression = compile_expression(binding_expression, &component);
init.push(quote!( init.push(quote!(
@ -1774,7 +1774,7 @@ impl<'a> LayoutTreeItem<'a> {
let path_layout_item_data = let path_layout_item_data =
|elem: &ElementRc, elem_rs: TokenStream, component_rust: TokenStream| { |elem: &ElementRc, elem_rs: TokenStream, component_rust: TokenStream| {
let prop_ref = |n: &str| { 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); let n = format_ident!("{}", n);
quote! {Some(& #elem_rs.#n)} quote! {Some(& #elem_rs.#n)}
} else { } else {
@ -1782,7 +1782,7 @@ impl<'a> LayoutTreeItem<'a> {
} }
}; };
let prop_value = |n: &str| { 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( let accessor = access_member(
&elem, &elem,
n, n,

View file

@ -7,8 +7,10 @@
This file is also available under commercial licensing terms. This file is also available under commercial licensing terms.
Please contact info@sixtyfps.io for more information. Please contact info@sixtyfps.io for more information.
LICENSE END */ LICENSE END */
use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap, HashSet}; 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::expression_tree::{Expression, Unit};
use crate::object_tree::Component; use crate::object_tree::Component;
@ -193,18 +195,39 @@ impl Type {
!matches!(self, Self::Duration | Self::Easing | Self::Angle) !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 { match self {
Type::Component(c) => c.root_element.borrow().lookup_property(name), Type::Component(c) => c.root_element.borrow().lookup_property(name),
Type::Builtin(b) => b.properties.get(name).cloned().unwrap_or_else(|| { Type::Builtin(b) => {
if b.is_non_item_type { let resolved_name =
Type::Invalid 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 { } else {
crate::typeregister::reserved_property(name) Cow::Borrowed(name)
} };
}), let property_type = n.lookup_property(resolved_name.as_ref()).unwrap_or_default();
Type::Native(n) => n.lookup_property(name).unwrap_or_default(), PropertyLookupResult { resolved_name, property_type }
_ => Type::Invalid, }
_ => PropertyLookupResult {
resolved_name: Cow::Borrowed(name),
property_type: Type::Invalid,
},
} }
} }
@ -392,6 +415,7 @@ pub struct NativeClass {
pub class_name: String, pub class_name: String,
pub vtable_symbol: String, pub vtable_symbol: String,
pub properties: HashMap<String, Type>, pub properties: HashMap<String, Type>,
pub deprecated_aliases: HashMap<String, String>,
pub cpp_type: Option<String>, pub cpp_type: Option<String>,
pub rust_type_constructor: Option<String>, pub rust_type_constructor: Option<String>,
} }
@ -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<Self>, name: &str) -> (usize, Rc<Self>) { fn lookup_property_distance(self: Rc<Self>, name: &str) -> (usize, Rc<Self>) {
let mut distance = 0; let mut distance = 0;
let mut class = self; 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); 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)] #[derive(Debug, Clone)]
pub struct Enumeration { pub struct Enumeration {
pub name: String, pub name: String,

View file

@ -9,9 +9,9 @@
LICENSE END */ LICENSE END */
//! Datastructures used to represent layouts in the compiler //! Datastructures used to represent layouts in the compiler
use crate::diagnostics::BuildDiagnostics;
use crate::langtype::Type; use crate::langtype::Type;
use crate::object_tree::{ElementRc, PropertyDeclaration}; use crate::object_tree::{ElementRc, PropertyDeclaration};
use crate::{diagnostics::BuildDiagnostics, langtype::PropertyLookupResult};
use crate::{ use crate::{
expression_tree::{Expression, NamedReference, Path}, expression_tree::{Expression, NamedReference, Path},
object_tree::Component, object_tree::Component,
@ -69,9 +69,11 @@ pub struct LayoutItem {
impl LayoutItem { impl LayoutItem {
pub fn rect(&self) -> Cow<LayoutRect> { pub fn rect(&self) -> Cow<LayoutRect> {
if let Some(e) = &self.element { if let Some(e) = &self.element {
let p = |name: &str| { let p = |unresolved_name: &str| {
if e.borrow().lookup_property(name) == Type::Length { let PropertyLookupResult { resolved_name, property_type } =
Some(NamedReference::new(e, name)) e.borrow().lookup_property(unresolved_name);
if property_type == Type::Length {
Some(NamedReference::new(e, resolved_name.as_ref()))
} else { } else {
None None
} }

View file

@ -81,11 +81,15 @@ pub fn load_builtins(register: &mut TypeRegister) {
let mut n = NativeClass::new_with_properties( let mut n = NativeClass::new_with_properties(
&id, &id,
e.PropertyDeclaration() e.PropertyDeclaration()
.map(|p| { .flat_map(|p| {
( if p.TwoWayBinding().is_some() {
identifier_text(&p.DeclaredIdentifier()).unwrap(), None // aliases are handled further down
object_tree::type_from_node(p.Type(), *diag.borrow_mut(), register), } else {
) Some((
identifier_text(&p.DeclaredIdentifier()).unwrap(),
object_tree::type_from_node(p.Type(), *diag.borrow_mut(), register),
))
}
}) })
.chain(e.CallbackDeclaration().map(|s| { .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.cpp_type = parse_annotation("cpp_type", &e.node).map(|x| x.unwrap());
n.rust_type_constructor = n.rust_type_constructor =
parse_annotation("rust_type_constructor", &e.node).map(|x| x.unwrap()); parse_annotation("rust_type_constructor", &e.node).map(|x| x.unwrap());

View file

@ -14,12 +14,13 @@ LICENSE END */
use crate::diagnostics::{FileDiagnostics, Spanned, SpannedWithSourceFile}; use crate::diagnostics::{FileDiagnostics, Spanned, SpannedWithSourceFile};
use crate::expression_tree::{self, Unit}; use crate::expression_tree::{self, Unit};
use crate::expression_tree::{Expression, ExpressionSpanned, NamedReference}; use crate::expression_tree::{Expression, ExpressionSpanned, NamedReference};
use crate::langtype::PropertyLookupResult;
use crate::langtype::{NativeClass, Type}; use crate::langtype::{NativeClass, Type};
use crate::parser::{identifier_text, syntax_nodes, SyntaxKind, SyntaxNodeWithSourceFile}; use crate::parser::{identifier_text, syntax_nodes, SyntaxKind, SyntaxNodeWithSourceFile};
use crate::typeregister::TypeRegister; use crate::typeregister::TypeRegister;
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use std::{borrow::Cow, cell::RefCell};
/// The full document (a complete file) /// The full document (a complete file)
#[derive(Default, Debug)] #[derive(Default, Debug)]
@ -418,9 +419,13 @@ impl Element {
for prop_decl in node.PropertyDeclaration() { for prop_decl in node.PropertyDeclaration() {
let type_node = prop_decl.Type(); let type_node = prop_decl.Type();
let prop_type = type_from_node(type_node.clone(), diag, tr); 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( diag.push_error(
format!("Cannot override property '{}'", prop_name), format!("Cannot override property '{}'", prop_name),
&prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(), &prop_decl.DeclaredIdentifier().child_token(SyntaxKind::Identifier).unwrap(),
@ -435,7 +440,7 @@ impl Element {
} }
r.property_declarations.insert( r.property_declarations.insert(
prop_name.clone(), prop_name.to_string(),
PropertyDeclaration { PropertyDeclaration {
property_type: prop_type, property_type: prop_type,
type_node: Some(type_node.into()), type_node: Some(type_node.into()),
@ -445,7 +450,7 @@ impl Element {
if let Some(csn) = prop_decl.BindingExpression() { if let Some(csn) = prop_decl.BindingExpression() {
if r.bindings if r.bindings
.insert(prop_name.clone(), ExpressionSpanned::new_uncompiled(csn.into())) .insert(prop_name.to_string(), ExpressionSpanned::new_uncompiled(csn.into()))
.is_some() .is_some()
{ {
diag.push_error( diag.push_error(
@ -456,7 +461,7 @@ impl Element {
} }
if let Some(csn) = prop_decl.TwoWayBinding() { if let Some(csn) = prop_decl.TwoWayBinding() {
if r.bindings if r.bindings
.insert(prop_name, ExpressionSpanned::new_uncompiled(csn.into())) .insert(prop_name.into(), ExpressionSpanned::new_uncompiled(csn.into()))
.is_some() .is_some()
{ {
diag.push_error( diag.push_error(
@ -504,18 +509,19 @@ impl Element {
} }
for con_node in node.CallbackConnection() { for con_node in node.CallbackConnection() {
let name = match identifier_text(&con_node) { let unresolved_name = match identifier_text(&con_node) {
Some(x) => x, Some(x) => x,
None => continue, None => continue,
}; };
let prop_type = r.lookup_property(&name); let PropertyLookupResult { resolved_name, property_type } =
if let Type::Callback { args, .. } = prop_type { r.lookup_property(&unresolved_name);
if let Type::Callback { args, .. } = property_type {
let num_arg = con_node.DeclaredIdentifier().count(); let num_arg = con_node.DeclaredIdentifier().count();
if num_arg > args.len() { if num_arg > args.len() {
diag.push_error( diag.push_error(
format!( format!(
"'{}' only has {} arguments, but {} were provided", "'{}' only has {} arguments, but {} were provided",
name, unresolved_name,
args.len(), args.len(),
num_arg num_arg
), ),
@ -523,7 +529,10 @@ impl Element {
); );
} }
if r.bindings 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() .is_some()
{ {
diag.push_error( diag.push_error(
@ -533,7 +542,7 @@ impl Element {
} }
} else { } else {
diag.push_error( 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(), &con_node.child_token(SyntaxKind::Identifier).unwrap(),
); );
} }
@ -548,17 +557,21 @@ impl Element {
}; };
for prop_name_token in anim.QualifiedName() { for prop_name_token in anim.QualifiedName() {
match QualifiedTypeName::from_node(prop_name_token.clone()).members.as_slice() { match QualifiedTypeName::from_node(prop_name_token.clone()).members.as_slice() {
[prop_name] => { [unresolved_prop_name] => {
let prop_type = r.lookup_property(&prop_name); let PropertyLookupResult { resolved_name, property_type } =
r.lookup_property(&unresolved_prop_name);
if let Some(anim_element) = animation_element_from_node( if let Some(anim_element) = animation_element_from_node(
&anim, &anim,
&prop_name_token, &prop_name_token,
prop_type, property_type,
diag, diag,
tr, tr,
) { ) {
if r.property_animations if r.property_animations
.insert(prop_name.clone(), PropertyAnimation::Static(anim_element)) .insert(
resolved_name.to_string(),
PropertyAnimation::Static(anim_element),
)
.is_some() .is_some()
{ {
diag.push_error("Duplicated animation".into(), &prop_name_token) diag.push_error("Duplicated animation".into(), &prop_name_token)
@ -750,13 +763,14 @@ impl Element {
e e
} }
/// Return the type of a property in this element or its base /// Return the type of a property in this element or its base, along with the final name, in case
pub fn lookup_property(&self, name: &str) -> Type { /// the provided name points towards a property alias. Type::Invalid is returned if the property does
self.property_declarations /// not exist.
.get(name) pub fn lookup_property<'a>(&self, name: &'a str) -> PropertyLookupResult<'a> {
.cloned() self.property_declarations.get(name).cloned().map(|decl| decl.property_type).map_or_else(
.map(|decl| decl.property_type) || self.base_type.lookup_property(name),
.unwrap_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 /// Return the Span of this element in the AST for error reporting
@ -773,26 +787,31 @@ impl Element {
diag: &mut FileDiagnostics, diag: &mut FileDiagnostics,
) { ) {
for (name_token, b) in bindings { for (name_token, b) in bindings {
let name = crate::parser::normalize_identifier(name_token.text()); let unresolved_name = crate::parser::normalize_identifier(name_token.text());
let prop_type = self.lookup_property(&name); let PropertyLookupResult { resolved_name, property_type } =
if !prop_type.is_property_type() { self.lookup_property(&unresolved_name);
if !property_type.is_property_type() {
diag.push_error( diag.push_error(
match prop_type { match property_type {
Type::Invalid => { Type::Invalid => {
format!("Unknown property {}{}", name, name_for_lookup_error) format!("Unknown property {}{}", unresolved_name, name_for_lookup_error)
} }
Type::Callback { .. } => { Type::Callback { .. } => {
format!("'{}' is a callback. Use `=>` to connect", name) format!("'{}' is a callback. Use `=>` to connect", unresolved_name)
} }
_ => format!( _ => format!(
"Cannot assign to {}{} because it does not have a valid property type.", "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, &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); diag.push_error("Duplicated property binding".into(), &name_token);
} }
} }
@ -917,25 +936,34 @@ fn lookup_property_from_qualified_name(
) -> (NamedReference, Type) { ) -> (NamedReference, Type) {
let qualname = QualifiedTypeName::from_node(node.clone()); let qualname = QualifiedTypeName::from_node(node.clone());
match qualname.members.as_slice() { match qualname.members.as_slice() {
[prop_name] => { [unresolved_prop_name] => {
let ty = r.borrow().lookup_property(prop_name.as_ref()); let PropertyLookupResult { resolved_name, property_type } =
if !ty.is_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); 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] => { [elem_id, unresolved_prop_name] => {
let (element, ty) = if let Some(element) = find_element_by_id(&r, elem_id.as_ref()) { let (element, name, ty) =
let ty = element.borrow().lookup_property(prop_name.as_ref()); if let Some(element) = find_element_by_id(&r, elem_id.as_ref()) {
if !ty.is_property_type() { let PropertyLookupResult { resolved_name, property_type } =
diag.push_error(format!("'{}' not found in '{}'", prop_name, elem_id), &node); element.borrow().lookup_property(unresolved_prop_name.as_ref());
} if !property_type.is_property_type() {
(Rc::downgrade(&element), ty) diag.push_error(
} else { format!("'{}' not found in '{}'", unresolved_prop_name, elem_id),
diag.push_error(format!("'{}' is not a valid element id", elem_id), &node); &node,
(Weak::new(), Type::Invalid) );
}; }
(NamedReference { element, name: prop_name.clone() }, ty) (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); 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); let mut bindings = std::mem::take(&mut elem.borrow_mut().bindings);
for (name, expr) in &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; elem.borrow_mut().bindings = bindings;
} }
@ -1068,7 +1096,12 @@ pub fn visit_element_expressions(
} }
for (ne, e) in &mut s.property_changes { for (ne, e) in &mut s.property_changes {
vis(e, Some(ne.name.as_ref()), &|| { 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
}); });
} }
} }

View file

@ -12,10 +12,13 @@ LICENSE END */
use std::rc::Rc; use std::rc::Rc;
use crate::expression_tree::{BuiltinFunction, Expression, NamedReference};
use crate::langtype::DefaultSizeBinding; use crate::langtype::DefaultSizeBinding;
use crate::langtype::Type; use crate::langtype::Type;
use crate::object_tree::{Component, ElementRc}; use crate::object_tree::{Component, ElementRc};
use crate::{
expression_tree::{BuiltinFunction, Expression, NamedReference},
langtype::PropertyLookupResult,
};
pub fn default_geometry(root_component: &Rc<Component>) { pub fn default_geometry(root_component: &Rc<Component>) {
crate::object_tree::recurse_elem_including_sub_components( crate::object_tree::recurse_elem_including_sub_components(
@ -44,11 +47,14 @@ pub fn default_geometry(root_component: &Rc<Component>) {
} }
fn make_default_100(elem: &ElementRc, parent_element: &ElementRc, property: &str) { 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; return;
} }
elem.borrow_mut().bindings.entry(property.into()).or_insert_with(|| { elem.borrow_mut().bindings.entry(resolved_name.to_string()).or_insert_with(|| {
Expression::PropertyReference(NamedReference::new(parent_element, property)).into() Expression::PropertyReference(NamedReference::new(parent_element, resolved_name.as_ref()))
.into()
}); });
} }

View file

@ -149,7 +149,9 @@ fn lower_transitions_in_element(
/// Returns a suitable unique name for the "state" property /// Returns a suitable unique name for the "state" property
fn compute_state_property_name(root_element: &ElementRc) -> String { fn compute_state_property_name(root_element: &ElementRc) -> String {
let mut property_name = "state".to_owned(); 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 += "_";
} }
property_name property_name
@ -168,5 +170,5 @@ fn expression_for_property(element: &ElementRc, name: &str) -> Expression {
None None
}; };
} }
Expression::default_value_for_type(&element.borrow().lookup_property(name)) Expression::default_value_for_type(&element.borrow().lookup_property(name).property_type)
} }

View file

@ -14,7 +14,6 @@ LICENSE END */
//! //!
//! Most of the code for the resolving actualy lies in the expression_tree module //! 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::expression_tree::*;
use crate::langtype::Type; use crate::langtype::Type;
use crate::object_tree::*; use crate::object_tree::*;
@ -22,6 +21,10 @@ use crate::parser::{
identifier_text, syntax_nodes, NodeOrTokenWithSourceFile, SyntaxKind, SyntaxNodeWithSourceFile, identifier_text, syntax_nodes, NodeOrTokenWithSourceFile, SyntaxKind, SyntaxNodeWithSourceFile,
}; };
use crate::typeregister::TypeRegister; use crate::typeregister::TypeRegister;
use crate::{
diagnostics::{BuildDiagnostics, SpannedWithSourceFile},
langtype::PropertyLookupResult,
};
use std::{collections::HashMap, rc::Rc}; use std::{collections::HashMap, rc::Rc};
/// This represeresent a scope for the Component, where Component is the repeated component, but /// 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); let mut parent = ctx.component_scope.last().and_then(find_parent_element);
while let Some(p) = parent { while let Some(p) = parent {
let ty = p.borrow().lookup_property(property_name); let PropertyLookupResult { resolved_name, property_type } =
if ty == Type::Length { p.borrow().lookup_property(property_name);
if property_type == Type::Length {
return Expression::BinaryExpression { return Expression::BinaryExpression {
lhs: Box::new(Expression::BinaryExpression { lhs: Box::new(Expression::BinaryExpression {
lhs: Box::new(e), lhs: Box::new(e),
@ -245,7 +249,7 @@ fn attempt_percent_conversion(
}), }),
rhs: Box::new(Expression::PropertyReference(NamedReference { rhs: Box::new(Expression::PropertyReference(NamedReference {
element: Rc::downgrade(&p), element: Rc::downgrade(&p),
name: property_name.to_string(), name: resolved_name.to_string(),
})), })),
op: '*', op: '*',
}; };
@ -577,22 +581,23 @@ impl Expression {
} }
} }
let property = elem.borrow().lookup_property(&first_str); let PropertyLookupResult { resolved_name, property_type } =
if property.is_property_type() { elem.borrow().lookup_property(&first_str);
if property_type.is_property_type() {
let prop = Self::PropertyReference(NamedReference { let prop = Self::PropertyReference(NamedReference {
element: Rc::downgrade(&elem), element: Rc::downgrade(&elem),
name: first_str, name: resolved_name.to_string(),
}); });
return maybe_lookup_object(prop, it, ctx); 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() { if let Some(x) = it.next() {
ctx.diag.push_error("Cannot access fields of callback".into(), &x) ctx.diag.push_error("Cannot access fields of callback".into(), &x)
} }
return Self::CallbackReference(NamedReference { return Self::CallbackReference(NamedReference {
element: Rc::downgrade(&elem), 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"); todo!("Continue looking up");
} }
} }
@ -681,8 +686,9 @@ impl Expression {
} }
} }
let property = elem.borrow().lookup_property(&first_str); let PropertyLookupResult { resolved_name: _, property_type } =
if property.is_property_type() { elem.borrow().lookup_property(&first_str);
if property_type.is_property_type() {
report_minus_error(ctx); report_minus_error(ctx);
return Expression::Invalid; return Expression::Invalid;
} }
@ -1128,23 +1134,24 @@ fn continue_lookup_within_element(
}; };
let prop_name = crate::parser::normalize_identifier(second.text()); let prop_name = crate::parser::normalize_identifier(second.text());
let p = elem.borrow().lookup_property(&prop_name); let PropertyLookupResult { resolved_name, property_type } =
if p.is_property_type() { elem.borrow().lookup_property(&prop_name);
if property_type.is_property_type() {
let prop = Expression::PropertyReference(NamedReference { let prop = Expression::PropertyReference(NamedReference {
element: Rc::downgrade(elem), element: Rc::downgrade(elem),
name: prop_name, name: resolved_name.to_string(),
}); });
maybe_lookup_object(prop, it, ctx) 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() { if let Some(x) = it.next() {
ctx.diag.push_error("Cannot access fields of callback".into(), &x) ctx.diag.push_error("Cannot access fields of callback".into(), &x)
} }
Expression::CallbackReference(NamedReference { Expression::CallbackReference(NamedReference {
element: Rc::downgrade(elem), element: Rc::downgrade(elem),
name: prop_name, name: resolved_name.to_string(),
}) })
} else if matches!(p, Type::Function{..}) { } else if matches!(property_type, Type::Function{..}) {
let member = elem.borrow().base_type.lookup_member_function(&prop_name); let member = elem.borrow().base_type.lookup_member_function(&resolved_name);
Expression::MemberFunction { Expression::MemberFunction {
base: Box::new(Expression::ElementReference(Rc::downgrade(elem))), base: Box::new(Expression::ElementReference(Rc::downgrade(elem))),
base_node: node.into(), base_node: node.into(),
@ -1169,7 +1176,9 @@ fn continue_lookup_within_element(
}; };
if let Some(minus_pos) = second.text().find('-') { if let Some(minus_pos) = second.text().find('-') {
// Attempt to recover if the user wanted to write "-" // 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."); err(" Use space before the '-' if you meant a substraction.");
return Expression::Invalid; return Expression::Invalid;
} }
@ -1213,15 +1222,16 @@ fn maybe_lookup_object(
} }
} }
Type::Component(c) => { Type::Component(c) => {
let prop_ty = c.root_element.borrow().lookup_property(next_str.as_str()); let PropertyLookupResult { resolved_name, property_type } =
if prop_ty != Type::Invalid { c.root_element.borrow().lookup_property(next_str.as_str());
if property_type != Type::Invalid {
base = Expression::ObjectAccess { base = Expression::ObjectAccess {
base: Box::new(std::mem::replace(&mut base, Expression::Invalid)), base: Box::new(std::mem::replace(&mut base, Expression::Invalid)),
name: next.to_string(), name: resolved_name.to_string(),
} }
} else { } else {
return error_or_try_minus(ctx, next, |x| { 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
}); });
} }
} }

View file

@ -24,7 +24,7 @@ TestCase := Rectangle {
animate * { } animate * { }
} }
in checked: { 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 { } animate color { }
} }
] ]

View file

@ -157,7 +157,7 @@ impl ItemWeak {
#[pin] #[pin]
/// The implementation of the `Rectangle` element /// The implementation of the `Rectangle` element
pub struct Rectangle { pub struct Rectangle {
pub color: Property<Color>, pub background: Property<Color>,
pub x: Property<f32>, pub x: Property<f32>,
pub y: Property<f32>, pub y: Property<f32>,
pub width: Property<f32>, pub width: Property<f32>,
@ -218,7 +218,7 @@ ItemVTable_static! {
#[pin] #[pin]
/// The implementation of the `BorderRectangle` element /// The implementation of the `BorderRectangle` element
pub struct BorderRectangle { pub struct BorderRectangle {
pub color: Property<Color>, pub background: Property<Color>,
pub x: Property<f32>, pub x: Property<f32>,
pub y: Property<f32>, pub y: Property<f32>,
pub width: Property<f32>, pub width: Property<f32>,

View file

@ -14,10 +14,10 @@ use core::ptr::NonNull;
use dynamic_type::{Instance, InstanceBox}; use dynamic_type::{Instance, InstanceBox};
use expression_tree::NamedReference; use expression_tree::NamedReference;
use object_tree::{Element, ElementRc}; use object_tree::{Element, ElementRc};
use sixtyfps_compilerlib::expression_tree::Expression;
use sixtyfps_compilerlib::langtype::Type; use sixtyfps_compilerlib::langtype::Type;
use sixtyfps_compilerlib::layout::{Layout, LayoutConstraints, LayoutItem, PathLayout}; use sixtyfps_compilerlib::layout::{Layout, LayoutConstraints, LayoutItem, PathLayout};
use sixtyfps_compilerlib::*; use sixtyfps_compilerlib::*;
use sixtyfps_compilerlib::{expression_tree::Expression, langtype::PropertyLookupResult};
use sixtyfps_corelib::component::{Component, ComponentRefPin, ComponentVTable}; use sixtyfps_corelib::component::{Component, ComponentRefPin, ComponentVTable};
use sixtyfps_corelib::graphics::{Rect, Resource}; use sixtyfps_corelib::graphics::{Rect, Resource};
use sixtyfps_corelib::item_tree::{ use sixtyfps_corelib::item_tree::{
@ -168,8 +168,10 @@ impl RepeatedComponent for ErasedComponentBox {
let root_item = &s.component_type.original.root_element.clone(); let root_item = &s.component_type.original.root_element.clone();
let get_prop = |name: &str| { let get_prop = |name: &str| {
if root_item.borrow().lookup_property(name) == Type::Length { let PropertyLookupResult { resolved_name, property_type } =
let nr = NamedReference::new(root_item, name); 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()); let p = get_property_ptr(&nr, s.borrow_instance());
// Safety: assuming get_property_ptr returned a valid pointer, // Safety: assuming get_property_ptr returned a valid pointer,
// we know that `Type::Length` is a property of type `f32` // we know that `Type::Length` is a property of type `f32`
@ -820,9 +822,10 @@ pub fn instantiate<'id>(
unsafe { unsafe {
let item = item_within_component.item_from_component(instance_ref.as_ptr()); let item = item_within_component.item_from_component(instance_ref.as_ptr());
let elem = item_within_component.elem.borrow(); let elem = item_within_component.elem.borrow();
for (prop, expr) in &elem.bindings { for (unresolved_prop_name, expr) in &elem.bindings {
let ty = elem.lookup_property(prop.as_str()); let PropertyLookupResult { resolved_name, property_type } =
if let Type::Callback { .. } = ty { elem.lookup_property(unresolved_prop_name.as_str());
if let Type::Callback { .. } = property_type {
let expr = expr.clone(); let expr = expr.clone();
let component_type = component_type.clone(); let component_type = component_type.clone();
let instance = component_box.instance.as_ptr(); let instance = component_box.instance.as_ptr();
@ -830,7 +833,8 @@ pub fn instantiate<'id>(
NonNull::from(&component_type.ct).cast(), NonNull::from(&component_type.ct).cast(),
instance.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( callback.set_handler(
item, item,
@ -845,7 +849,7 @@ pub fn instantiate<'id>(
}), }),
) )
} else if let Some(callback_offset) = } 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()); let callback = callback_offset.apply(instance_ref.as_ref());
callback.set_handler(move |args| { callback.set_handler(move |args| {
@ -857,13 +861,14 @@ pub fn instantiate<'id>(
eval::eval_expression(&expr, &mut local_context) eval::eval_expression(&expr, &mut local_context)
}) })
} else { } else {
panic!("unkown callback {}", prop) panic!("unkown callback {}", unresolved_prop_name)
} }
} else { } else {
if let Some(prop_rtti) = 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); let mut e = Some(&expr.expression);
while let Some(Expression::TwoWayBinding(nr, next)) = &e { while let Some(Expression::TwoWayBinding(nr, next)) = &e {
// Safety: The compiler must have ensured that the properties exist and are of the same type // 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 { } else if let Some(PropertiesWithinComponent {
offset, prop: prop_info, .. 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( let c = Pin::new_unchecked(vtable::VRef::from_raw(
NonNull::from(&component_type.ct).cast(), NonNull::from(&component_type.ct).cast(),
component_box.instance.as_ptr().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), .. } Type::Object { name: Some(name), .. }
if name.ends_with("::StateInfo") => if name.ends_with("::StateInfo") =>
{ {
@ -946,7 +951,7 @@ pub fn instantiate<'id>(
let maybe_animation = animation_for_property( let maybe_animation = animation_for_property(
instance_ref, instance_ref,
&component_type.original.root_element.borrow(), &component_type.original.root_element.borrow(),
prop, resolved_name.as_ref(),
); );
let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(*offset)); let item = Pin::new_unchecked(&*instance_ref.as_ptr().add(*offset));
@ -983,7 +988,7 @@ pub fn instantiate<'id>(
} }
} }
} else { } else {
panic!("unkown property {}", prop); panic!("unkown property {}", unresolved_prop_name);
} }
} }
} }

View file

@ -667,7 +667,7 @@ impl ItemRenderer for GLItemRenderer {
} }
// TODO: cache path in item to avoid re-tesselation // TODO: cache path in item to avoid re-tesselation
let mut path = rect_to_path(geometry); 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| { self.shared_data.canvas.borrow_mut().save_with(|canvas| {
canvas.translate(pos.x, pos.y); canvas.translate(pos.x, pos.y);
canvas.fill_path(&mut path, paint) canvas.fill_path(&mut path, paint)
@ -705,7 +705,7 @@ impl ItemRenderer for GLItemRenderer {
path.rounded_rect(x, y, width, height, border_radius); 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()); let mut border_paint = femtovg::Paint::color(rect.border_color().into());
border_paint.set_line_width(border_width); border_paint.set_line_width(border_width);

View file

@ -226,7 +226,7 @@ struct QtItemRenderer<'a> {
impl ItemRenderer for QtItemRenderer<'_> { impl ItemRenderer for QtItemRenderer<'_> {
fn draw_rectangle(&mut self, pos: Point, rect: Pin<&items::Rectangle>) { fn draw_rectangle(&mut self, pos: Point, rect: Pin<&items::Rectangle>) {
let pos = qttypes::QPoint { x: pos.x as _, y: pos.y as _ }; 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 rect: qttypes::QRectF = get_geometry!(pos, items::Rectangle, rect);
let painter: &mut QPainter = &mut *self.painter; let painter: &mut QPainter = &mut *self.painter;
cpp! { unsafe [painter as "QPainter*", color as "QRgb", rect as "QRectF"] { 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>) { fn draw_border_rectangle(&mut self, pos: Point, rect: std::pin::Pin<&items::BorderRectangle>) {
self.draw_rectangle_impl( self.draw_rectangle_impl(
get_geometry!(pos, items::BorderRectangle, rect), get_geometry!(pos, items::BorderRectangle, rect),
rect.color(), rect.background(),
rect.border_color(), rect.border_color(),
rect.border_width(), rect.border_width(),
rect.border_radius(), rect.border_radius(),