New syntax: allow to create component without base

This commit is contained in:
Olivier Goffart 2022-10-26 16:26:55 +02:00 committed by Olivier Goffart
parent d7e366864b
commit f055afd2de
14 changed files with 187 additions and 77 deletions

View file

@ -23,13 +23,19 @@
import { _ } from ""; // just to silence many errors in the LSP
Rectangle := _ {
property <brush> background;
property <brush> color <=> background;
Empty := _ {
property <length> x;
property <length> y;
property <length> width;
property <length> height;
//-default_size_binding:expands_to_parent_geometry
//-is_internal
}
Rectangle := Empty {
property <brush> background;
property <brush> color <=> background;
}
BorderRectangle := Rectangle {
@ -41,12 +47,8 @@ BorderRectangle := Rectangle {
export { BorderRectangle as Rectangle }
ImageItem := _ {
ImageItem := Empty {
property <image> source;
property <length> x;
property <length> y;
property <length> width;
property <length> height;
property <ImageFit> image-fit;
property <ImageRendering> image-rendering;
}
@ -57,25 +59,20 @@ export ClippedImage := ImageItem {
property <int> source-clip-width;
property <int> source-clip-height;
property <brush> colorize;
property <ImageRendering> image-rendering;
//-default_size_binding:implicit_size
}
export { ClippedImage as Image }
export Rotate := _ {
property <length> x;
property <length> y;
export Rotate := Empty {
property <angle> rotation-angle;
property <length> rotation-origin-x;
property <length> rotation-origin-y;
property <length> width;
property <length> height;
//-default_size_binding:expands_to_parent_geometry
//-is_internal
}
export Text := _ {
export Text := Empty {
property <string> text;
property <string> font-family;
property <length> font-size;
@ -86,10 +83,6 @@ export Text := _ {
property <TextOverflow> overflow;
property <TextWrap> wrap;
property <length> letter-spacing;
property <length> x;
property <length> y;
property <length> width;
property <length> height;
//-default_size_binding:implicit_size
}
@ -146,11 +139,7 @@ export FocusScope := _ {
//-accepts_focus
}
export Flickable := _ {
property <length> x;
property <length> y;
property <length> width;
property <length> height;
export Flickable := Empty {
// These properties are actually going to be forwarded to the viewport by the
// code generator
property <length> viewport-height;
@ -176,11 +165,7 @@ WindowItem := _ {
export Window := WindowItem {}
export BoxShadow := _ {
property <length> x;
property <length> y;
property <length> width;
property <length> height;
export BoxShadow := Empty {
property <length> border_radius;
property <length> offset_x;
property <length> offset_y;
@ -250,11 +235,7 @@ export Opacity := _ {
//-is_internal
}
export Layer := _ {
property <length> x;
property <length> y;
property <length> width;
property <length> height;
export Layer := Empty {
property <bool> cache-rendering-hint;
//-default_size_binding:expands_to_parent_geometry
//-is_internal

View file

@ -209,6 +209,8 @@ pub fn load_builtins(register: &mut TypeRegister) {
register.property_animation_type =
ElementType::Builtin(natives.remove("PropertyAnimation").unwrap());
register.empty_type = ElementType::Builtin(natives.remove("Empty").unwrap());
if !diag.is_empty() {
let vec = diag.to_string_vec();
#[cfg(feature = "display-diagnostics")]

View file

@ -254,7 +254,11 @@ impl Component {
root_element: Element::from_node(
node.Element(),
"root".into(),
ElementType::Error,
if node.child_text(SyntaxKind::Identifier).map_or(false, |t| t == "global") {
ElementType::Global
} else {
ElementType::Error
},
&mut child_insertion_point,
diag,
tr,
@ -681,13 +685,7 @@ impl Element {
ElementType::Error
}
}
} else {
if parent_type != ElementType::Error {
// This should normally never happen because the parser does not allow for this
assert!(diag.has_error());
return ElementRc::default();
}
} else if parent_type == ElementType::Global {
// This must be a global component it can only have properties and callback
let mut error_on = |node: &dyn Spanned, what: &str| {
diag.push_error(format!("A global component cannot have {}", what), node);
@ -700,7 +698,14 @@ impl Element {
node.PropertyAnimation().for_each(|n| error_on(&n, "animations"));
node.States().for_each(|n| error_on(&n, "states"));
node.Transitions().for_each(|n| error_on(&n, "transitions"));
ElementType::Global
} else if parent_type != ElementType::Error {
// This should normally never happen because the parser does not allow for this
assert!(diag.has_error());
return ElementRc::default();
} else {
tr.empty_type()
};
let mut r = Element { id, base_type, node: Some(node.clone()), ..Default::default() };
@ -1158,10 +1163,11 @@ impl Element {
match lookup_result.property_type {
Type::Invalid => {
if self.base_type != ElementType::Error {
diag.push_error(format!(
"Unknown property {} in {}",
unresolved_name, self.base_type
),
diag.push_error(if self.base_type.to_string() == "Empty" {
format!( "Unknown property {unresolved_name}")
} else {
format!( "Unknown property {unresolved_name} in {}", self.base_type)
},
&name_token);
}
}

View file

@ -81,12 +81,17 @@ pub fn parse_component(p: &mut impl Parser) -> bool {
return false;
}
} else {
if p.peek().as_str() != "inherits" {
p.error("Expected keyword 'inherits'");
if p.peek().as_str() == "inherits" {
p.consume();
} else if p.peek().kind() == SyntaxKind::LBrace {
let mut p = p.start_node(SyntaxKind::Element);
p.consume();
parse_element_content(&mut *p);
return p.expect(SyntaxKind::RBrace);
} else {
p.error("Expected '{' or keyword 'inherits'");
drop(p.start_node(SyntaxKind::Element));
return false;
} else {
p.consume();
}
}

View file

@ -23,9 +23,9 @@ pub fn is_flickable_element(element: &ElementRc) -> bool {
}
pub fn handle_flickable(root_component: &Rc<Component>, tr: &TypeRegister) {
let mut native_rect = tr.lookup_element("Rectangle").unwrap().as_builtin().native_class.clone();
while let Some(p) = native_rect.parent.clone() {
native_rect = p;
let mut native_empty = tr.empty_type().as_builtin().native_class.clone();
while let Some(p) = native_empty.parent.clone() {
native_empty = p;
}
crate::object_tree::recurse_elem_including_sub_components(
@ -37,17 +37,17 @@ pub fn handle_flickable(root_component: &Rc<Component>, tr: &TypeRegister) {
}
fixup_geometry(elem);
create_viewport_element(elem, &native_rect);
create_viewport_element(elem, &native_empty);
},
)
}
fn create_viewport_element(flickable_elem: &ElementRc, native_rect: &Rc<NativeClass>) {
fn create_viewport_element(flickable_elem: &ElementRc, native_empty: &Rc<NativeClass>) {
let mut flickable = flickable_elem.borrow_mut();
let flickable = &mut *flickable;
let viewport = Rc::new(RefCell::new(Element {
id: format!("{}-viewport", flickable.id),
base_type: ElementType::Native(native_rect.clone()),
base_type: ElementType::Native(native_empty.clone()),
children: std::mem::take(&mut flickable.children),
enclosing_component: flickable.enclosing_component.clone(),
is_flickable_viewport: true,

View file

@ -90,7 +90,7 @@ fn should_materialize(
if property_declarations.contains_key(prop) {
return None;
}
let has_declared_property = match &base_type {
let has_declared_property = match base_type {
ElementType::Component(c) => has_declared_property(&c.root_element.borrow(), prop),
ElementType::Builtin(b) => b.properties.contains_key(prop),
ElementType::Native(n) => {

View file

@ -48,6 +48,9 @@ fn can_optimize(elem: &ElementRc) -> bool {
let base_type = match &e.base_type {
ElementType::Builtin(base_type) if base_type.name == "Rectangle" => base_type,
ElementType::Builtin(base_type) if base_type.native_class.class_name == "Empty" => {
base_type
}
_ => return false,
};

View file

@ -128,11 +128,11 @@ fn select_minimal_class() {
["x".to_owned(), "width".to_owned()].iter()
)
.class_name,
"Rectangle",
"Empty",
);
assert_eq!(
select_minimal_class_based_on_property_usage(&rect.native_class, [].iter()).class_name,
"Rectangle",
"Empty",
);
assert_eq!(
select_minimal_class_based_on_property_usage(

View file

@ -0,0 +1,18 @@
// Copyright © SixtyFPS GmbH <info@slint-ui.com>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial
component Foo {
background: 0;
// ^error{Unknown property background$}
width: 12px;
}
component Bar {
Foo { }
Foo {
background: 0;
// ^error{Unknown property background in Foo}
height: self.width;
}
}

View file

@ -191,6 +191,7 @@ pub struct TypeRegister {
elements: HashMap<String, ElementType>,
supported_property_animation_types: HashSet<String>,
pub(crate) property_animation_type: ElementType,
pub(crate) empty_type: ElementType,
/// Map from a context restricted type to the list of contexts (parent type) it is allowed in. This is
/// used to construct helpful error messages, such as "Row can only be within a GridLayout element".
context_restricted_types: HashMap<String, HashSet<String>>,
@ -368,4 +369,11 @@ impl TypeRegister {
}
all
}
pub fn empty_type(&self) -> ElementType {
match self.parent_registry.as_ref() {
Some(parent) => parent.borrow().empty_type(),
None => self.empty_type.clone(),
}
}
}