Begin with the generation of rhe repeater (rust only for now)

This commit is contained in:
Olivier Goffart 2020-06-16 13:42:45 +02:00
parent 064db5aa5b
commit 6122f91fba
11 changed files with 162 additions and 50 deletions

View file

@ -72,6 +72,7 @@ pub mod re_exports {
pub use const_field_offset::{self, FieldOffsets};
pub use once_cell::sync::Lazy;
pub use sixtyfps_corelib::abi::datastructures::*;
pub use sixtyfps_corelib::abi::model::*;
pub use sixtyfps_corelib::abi::primitives::*;
pub use sixtyfps_corelib::abi::properties::Property;
pub use sixtyfps_corelib::abi::signals::Signal;

View file

@ -38,11 +38,25 @@ component ButtonRectangle := Rectangle {
text: button_text;
color: black;
}
color: { button_area.pressed ? red : green; }
color: { button_area.pressed ? red : #5898; }
}
Hello := Rectangle {
for x in 8: Rectangle {
color: #8005;
width: 75;
height: 75;
Rectangle {
color: #00f5;
width: 25;
height: 25;
x: 25;
y: 25;
}
}
signal foobar;
signal plus_clicked;
signal minus_clicked;
@ -50,12 +64,7 @@ Hello := Rectangle {
color: white;
TwoRectangle {
width: 100;
height: 100;
color: blue;
clicked => { foobar() }
}
Rectangle {
x: 100;
y: 100;
@ -77,7 +86,7 @@ Hello := Rectangle {
}
ButtonRectangle {
color: #8a8;
color: blue;
x: 50;
y: 225;
clicked => { counter += 1 }
@ -85,7 +94,7 @@ Hello := Rectangle {
}
counter_label := Text { x: 100; y: 300; text: counter; color: black; }
ButtonRectangle {
color: #aa8;
color: yellow;
x: 50;
y: 350;
clicked => { minus_clicked() }

View file

@ -146,6 +146,11 @@ impl CppType for Type {
}
fn handle_item(item: &Element, main_struct: &mut Struct, init: &mut Vec<String>) {
if item.repeated.is_some() {
println!("FIXME: implement for");
return;
}
main_struct.members.push(Declaration::Var(Var {
ty: format!("sixtyfps::{}", item.base_type.as_builtin().class_name),
name: item.id.clone(),
@ -324,7 +329,7 @@ pub fn generate(component: &Component, diag: &mut Diagnostics) -> Option<impl st
children_offset,
)
} else {
todo!()
println!("FIXME: implement for")
}
});

View file

@ -32,6 +32,7 @@ impl RustType for PropertyDeclaration {
///
/// Fill the diagnostic in case of error.
pub fn generate(component: &Component, diag: &mut Diagnostics) -> Option<TokenStream> {
let mut extra_components = vec![];
let mut declared_property_var_names = vec![];
let mut declared_property_vars = vec![];
let mut declared_property_types = vec![];
@ -96,16 +97,58 @@ pub fn generate(component: &Component, diag: &mut Diagnostics) -> Option<TokenSt
return None;
}
// Fixme! Ideally we would still have the spans available
let component_id = quote::format_ident!("{}", component.id);
let component_id = component_id(component);
let mut item_tree_array = Vec::new();
let mut item_names = Vec::new();
let mut item_types = Vec::new();
let mut repeated_element_names = Vec::new();
let mut repeated_element_components = Vec::new();
let mut repeated_visit_branch = Vec::new();
let mut init = Vec::new();
super::build_array_helper(component, |item, children_index| {
let item = item.borrow();
if item.repeated.is_none() {
if let Some(repeated) = &item.repeated {
let base_component = match &item.base_type {
Type::Component(c) => c,
_ => panic!("should be a component because of the repeater_component pass"),
};
let repeater_index = repeated_element_names.len();
let repeater_id = quote::format_ident!("repeater_{}", repeater_index);
let rep_component_id = self::component_id(&*base_component);
extra_components.push(generate(&*base_component, diag).unwrap_or_default());
extra_components.push(quote! {
impl sixtyfps::re_exports::RepeatedComponent for #rep_component_id {
type Data = (); // FIXME
fn update(&self, _index: usize, _data: &Self::Data) {
// FIXME()
}
}
});
assert!(
repeated.model.is_constant()
&& matches!(repeated.model.ty(), Type::Int32 | Type::Float32),
"TODO: currently model can only be integers"
);
let count = compile_expression(&repeated.model, component);
init.push(quote!(self_.#repeater_id.update_model(&[() ; (#count) as usize ]);));
item_tree_array.push(quote!(
sixtyfps::re_exports::ItemTreeNode::DynamicTree {
index: #repeater_index,
}
));
repeated_visit_branch.push(quote!(
#repeater_index => base.#repeater_id.visit(visitor),
));
repeated_element_names.push(repeater_id);
repeated_element_components.push(rep_component_id);
} else {
let field_name = quote::format_ident!("{}", item.id);
let children_count = item.children.len() as u32;
item_tree_array.push(quote!(
@ -113,7 +156,7 @@ pub fn generate(component: &Component, diag: &mut Diagnostics) -> Option<TokenSt
item: VOffset::new(#component_id::field_offsets().#field_name),
chilren_count: #children_count,
children_index: #children_index,
}
}
));
for (k, binding_expression) in &item.bindings {
let rust_property_ident = quote::format_ident!("{}", k);
@ -149,8 +192,6 @@ pub fn generate(component: &Component, diag: &mut Diagnostics) -> Option<TokenSt
}
item_names.push(field_name);
item_types.push(quote::format_ident!("{}", item.base_type.as_builtin().class_name));
} else {
todo!()
}
});
@ -176,6 +217,7 @@ pub fn generate(component: &Component, diag: &mut Diagnostics) -> Option<TokenSt
#(#item_names : sixtyfps::re_exports::#item_types,)*
#(#declared_property_vars : sixtyfps::re_exports::Property<#declared_property_types>,)*
#(#declared_signals : sixtyfps::re_exports::Signal<()>,)*
#(#repeated_element_names : sixtyfps::re_exports::Repeater<#repeated_element_components>,)*
}
impl core::default::Default for #component_id {
@ -185,6 +227,7 @@ pub fn generate(component: &Component, diag: &mut Diagnostics) -> Option<TokenSt
#(#item_names : Default::default(),)*
#(#declared_property_vars : Default::default(),)*
#(#declared_signals : Default::default(),)*
#(#repeated_element_names : Default::default(),)*
};
#(#init)*
self_
@ -195,7 +238,13 @@ pub fn generate(component: &Component, diag: &mut Diagnostics) -> Option<TokenSt
fn visit_children_item(&self, index: isize, visitor: sixtyfps::re_exports::ItemVisitorRefMut) {
use sixtyfps::re_exports::*;
let tree = &[#(#item_tree_array),*];
sixtyfps::re_exports::visit_item_tree(self, VRef::new(self), tree, index, visitor);
sixtyfps::re_exports::visit_item_tree(self, VRef::new(self), tree, index, visitor, visit_dynamic);
fn visit_dynamic(base: &#component_id, visitor: ItemVisitorRefMut, dyn_index: usize) {
match dyn_index {
#(#repeated_visit_branch)*
_ => panic!("invalid dyn_index {}", dyn_index),
}
}
}
fn create() -> Self {
Default::default()
@ -213,9 +262,20 @@ pub fn generate(component: &Component, diag: &mut Diagnostics) -> Option<TokenSt
#(#property_and_signal_accessors)*
}
#(#extra_components)*
))
}
/// Return an identifier suitable for this component
fn component_id(component: &Component) -> proc_macro2::Ident {
if component.id.is_empty() {
quote::format_ident!("{}", component.root_element.borrow().id)
} else {
quote::format_ident!("{}", component.id)
}
}
fn access_member(element: &ElementRc, name: &str) -> TokenStream {
let e = element.borrow();
let name_ident = quote::format_ident!("{}", name);

View file

@ -26,6 +26,7 @@ mod passes {
pub mod inlining;
pub mod lower_layout;
pub mod move_declarations;
pub mod repeater_component;
pub mod resolving;
pub mod unique_id;
}
@ -45,8 +46,9 @@ pub fn run_passes(
passes::inlining::inline(doc);
passes::lower_layout::lower_layouts(&doc.root_component, diag);
passes::unique_id::assign_unique_id(&doc.root_component);
passes::move_declarations::move_declarations(&doc.root_component);
if compiler_config.embed_resources {
passes::collect_resources::collect_resources(&doc.root_component);
}
passes::repeater_component::create_repeater_components(&doc.root_component, diag);
passes::move_declarations::move_declarations(&doc.root_component);
}

View file

@ -50,8 +50,7 @@ pub fn resolve_expressions(doc: &Document, diag: &mut Diagnostics, tr: &mut Type
component: component.clone(),
diag,
};
r.model =
Expression::from_binding_expression_node(node.clone(), &mut lookup_ctx)
r.model = Expression::from_expression_node(node.clone(), &mut lookup_ctx)
}
}
elem.borrow_mut().repeated = repeated;

View file

@ -96,22 +96,8 @@ pub enum ItemTreeNode<T> {
/// A placeholder for many instance of item in their own component which
/// are instentiated according to a model.
DynamicTree {
/// Component vtable.
/// This component is going to be instantiated as many time as the model tells
component_type: *const ComponentVTable,
/// vtable of the model
model_type: *const super::model::ModelType,
/// byte offset of the ModelImpl within the component.
/// The model is an instance of the model described by model_type and must be
/// stored within the component
model_offset: isize,
/// byte offset of the vector of components within the parent component
/// (ComponentVec)
/// a ComponentVec must be stored within the component to represent this tree
components_holder_offset: isize,
/// the undex which is passed in the visit_dynamic callback.
index: usize,
},
}
@ -320,12 +306,16 @@ pub unsafe extern "C" fn sixtyfps_visit_item_tree(
index: isize,
visitor: VRefMut<ItemVisitorVTable>,
) {
fn visit_dynamic(_base: &u8, _visitor: vtable::VRefMut<ItemVisitorVTable>, _dyn_index: usize) {
todo!()
}
crate::item_tree::visit_item_tree(
&*(component.as_ptr() as *const u8),
component,
item_tree.as_slice(),
index,
visitor,
visit_dynamic,
)
}

View file

@ -1,7 +1,7 @@
//! Models
/// Opaque type of a model, represented bith a ModelType
pub struct ModelImpl;
/*
use super::datastructures::ComponentVTable;
/// Virtual table for a model.
///
@ -9,18 +9,58 @@ pub struct ModelImpl;
///
/// TODO: how to get notification when it changes
#[repr(C)]
pub struct ModelType {
#[vtable]
pub struct ModelVTable {
/// Number of items
count: unsafe fn(*const ModelType, *const ModelImpl) -> u32,
count: unsafe fn(VRef<ModelVTable>) -> u32,
/// Returns the data. (FIXME: find out what this returns exactly)
data: unsafe fn(*const ModelType, *const ModelImpl, n: u32) -> *const (),
}
data: unsafe fn(VRef<ModelVTable>, n: u32) -> *const (),
}*/
/*
/// This structure will hold a vector of the component instaces
#[repr(C)]
pub struct ComponentVec {
/// Should be some kind of smart pointer that deletes the component
/// Should also be a repr(c) thing with some kind of init method
_todo: Vec<*mut dyn super::datastructures::Component>,
pub struct ComponentVecHolder {
mode: vtable::VBox<ModelType>
// Possible optimization: all the VBox should have the same VTable kown to the parent component
_todo: Vec<vtable::VBox<super::datastructures::ComponentVTable>>,
}
*/
/// Component that can be instantiated by a repeater.
pub trait RepeatedComponent: crate::abi::datastructures::Component {
/// The data corresponding to the model
type Data;
/// Update this component at the given index and the given data
fn update(&self, index: usize, data: &Self::Data);
}
/// This field is put in a component when using the `for` syntax
/// It helps instantiating the components `C`
#[derive(Default)]
pub struct Repeater<C> {
components: Vec<Box<C>>,
}
impl<Data, C> Repeater<C>
where
C: RepeatedComponent<Data = Data>,
{
/// Called when the model is changed
pub fn update_model(&mut self, data: &[Data]) {
self.components.clear();
for (i, d) in data.iter().enumerate() {
let c = C::create();
c.update(i, d);
self.components.push(Box::new(c));
}
}
/// Call the visitor for each component
pub fn visit(&self, mut visitor: super::datastructures::ItemVisitorRefMut) {
for c in &self.components {
c.visit_children_item(-1, visitor.borrow_mut());
}
}
}

View file

@ -34,12 +34,13 @@ pub fn visit_item_tree<Base>(
item_tree: &[ItemTreeNode<Base>],
index: isize,
mut visitor: vtable::VRefMut<ItemVisitorVTable>,
visit_dynamic: fn(base: &Base, visitor: vtable::VRefMut<ItemVisitorVTable>, dyn_index: usize),
) {
let mut visit_at_index = |idx: usize| match &item_tree[idx] {
ItemTreeNode::Item { item, .. } => {
visitor.visit_item(component, idx as isize, item.apply(base));
}
_ => todo!(),
ItemTreeNode::DynamicTree { index } => visit_dynamic(base, visitor.borrow_mut(), *index),
};
if index == -1 {
visit_at_index(0);

View file

@ -81,7 +81,11 @@ unsafe extern "C" fn visit_children_item(
item_tree.as_slice().into(),
index,
v,
visit_dynamic,
);
fn visit_dynamic(base: &Instance, visitor: ItemVisitorRefMut, dyn_index: usize) {
todo!()
}
}
/// Information attached to a builtin item
@ -153,7 +157,8 @@ pub fn load(
generator::build_array_helper(&tree.root_component, |rc_item, child_offset| {
let item = rc_item.borrow();
if item.repeated.is_some() {
todo!()
println!("FIXME: implement for");
return;
}
let rt = &rtti[&*item.base_type.as_builtin().class_name];
let offset = builder.add_field(rt.type_info);

View file

@ -15,7 +15,7 @@ fn main() -> std::io::Result<()> {
let module_name =
testcase.relative_path.file_stem().unwrap().to_string_lossy().replace("/", "_");
write!(generated_file, "#[path=\"{0}.rs\"] mod {0};\n", module_name)?;
write!(generated_file, "#[path=\"{0}.rs\"] mod r#{0};\n", module_name)?;
let source = std::fs::read_to_string(&testcase.absolute_path)?;
let mut output = std::fs::File::create(