mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-02 22:54:36 +00:00
Begin with the generation of rhe repeater (rust only for now)
This commit is contained in:
parent
064db5aa5b
commit
6122f91fba
11 changed files with 162 additions and 50 deletions
|
@ -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;
|
||||
|
|
|
@ -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() }
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue