diff --git a/api/sixtyfps-cpp/include/sixtyfps.h b/api/sixtyfps-cpp/include/sixtyfps.h index 31c38dcde..48af3bada 100644 --- a/api/sixtyfps-cpp/include/sixtyfps.h +++ b/api/sixtyfps-cpp/include/sixtyfps.h @@ -73,7 +73,6 @@ constexpr inline ItemTreeNode make_dyn_node(std::uintptr_t offset) using internal::sixtyfps_visit_item_tree; // layouts: -using internal::Constraint; using internal::GridLayoutCellData; using internal::GridLayoutData; using internal::PathLayoutData; diff --git a/api/sixtyfps-rs/lib.rs b/api/sixtyfps-rs/lib.rs index 9ebc66a0d..bd8f96be0 100644 --- a/api/sixtyfps-rs/lib.rs +++ b/api/sixtyfps-rs/lib.rs @@ -81,8 +81,8 @@ pub mod re_exports { pub use sixtyfps_corelib::abi::slice::Slice; pub use sixtyfps_corelib::item_tree::visit_item_tree; pub use sixtyfps_corelib::layout::{ - solve_grid_layout, solve_path_layout, Constraint, GridLayoutCellData, GridLayoutData, - PathLayoutData, PathLayoutItemData, + solve_grid_layout, solve_path_layout, GridLayoutCellData, GridLayoutData, PathLayoutData, + PathLayoutItemData, }; pub use sixtyfps_corelib::Color; pub use sixtyfps_corelib::ComponentVTable_static; diff --git a/sixtyfps_compiler/generator/cpp.rs b/sixtyfps_compiler/generator/cpp.rs index f97ee9392..3e15719e4 100644 --- a/sixtyfps_compiler/generator/cpp.rs +++ b/sixtyfps_compiler/generator/cpp.rs @@ -892,56 +892,32 @@ fn compute_layout(component: &Rc) -> Vec { )); for grid in component.layout_constraints.borrow().grids.iter() { res.push("{".to_owned()); - res.push(format!(" std::array row_constr;", grid.row_count())); - res.push(format!(" std::array col_constr;", grid.col_count())); - res.push( - " row_constr.fill(sixtyfps::Constraint{0., std::numeric_limits::max()});" - .to_owned(), - ); - res.push( - " col_constr.fill(sixtyfps::Constraint{0., std::numeric_limits::max()});" - .to_owned(), - ); res.push(" sixtyfps::GridLayoutCellData grid_data[] = {".to_owned()); - let mut row_info = vec![]; - let mut count = 0; - for row in &grid.elems { - row_info.push(format!("{{ &grid_data[{}], {} }}", count, row.len())); - for cell in row { - if let Some(cell) = cell { - let p = |n: &str| { - if cell.borrow().lookup_property(n) == Type::Length { - format!("&self->{}.{}", cell.borrow().id, n) - } else { - "nullptr".to_owned() - } - }; - res.push(format!( - " {{ {}, {}, {}, {} }},", - p("x"), - p("y"), - p("width"), - p("height") - )); + for cell in &grid.elems { + let p = |n: &str| { + if cell.item.borrow().lookup_property(n) == Type::Length { + format!("&self->{}.{}", cell.item.borrow().id, n) } else { - res.push(" {},".into()); + "nullptr".to_owned() } - count += 1; - } + }; + res.push(format!( + " {{ {c}, {r}, {cs}, {rs}, sixtyfps::{vt}.layouting_info({{&sixtyfps::{vt}, const_cast(&self->{id})}}), {x}, {y}, {w}, {h} }},", + c = cell.col, r = cell.row, cs = cell.colspan, rs =cell.rowspan, + vt = cell.item.borrow().base_type.as_native().vtable_symbol, + ty = cell.item.borrow().base_type.as_native().class_name, + id = cell.item.borrow().id, + x = p("x"), y = p("y"), w = p("width"), h = p("height") + )); } res.push(" };".to_owned()); - res.push(" sixtyfps::Slice cells[] = {".to_owned()); - res.push(format!(" {} }};", row_info.join(", "))); res.push(format!(" auto x = {};", compile_expression(&grid.x_reference, component))); res.push(format!(" auto y = {};", compile_expression(&grid.y_reference, component))); res.push(" sixtyfps::GridLayoutData grid { ".into()); - // FIXME: add auto conversion from std::array* to Slice - res.push(" { row_constr.data(), row_constr.size() },".to_owned()); - res.push(" { col_constr.data(), col_constr.size() },".to_owned()); res.push(format!(" self->{}.width.get(),", grid.within.borrow().id)); res.push(format!(" self->{}.height.get(),", grid.within.borrow().id)); res.push(" x, y,".to_owned()); - res.push(" {cells, std::size(cells)}".to_owned()); + res.push(" {grid_data, std::size(grid_data)}".to_owned()); res.push(" };".to_owned()); res.push(" sixtyfps::solve_grid_layout(&grid);".to_owned()); res.push("}".to_owned()); diff --git a/sixtyfps_compiler/generator/rust.rs b/sixtyfps_compiler/generator/rust.rs index f7cbbca19..bdb4fff9a 100644 --- a/sixtyfps_compiler/generator/rust.rs +++ b/sixtyfps_compiler/generator/rust.rs @@ -720,56 +720,46 @@ fn compute_layout(component: &Rc) -> TokenStream { "{}", grid_layout.within.borrow().base_type.as_native().class_name ); - let row_constraint = vec![quote!(Constraint::default()); grid_layout.row_count()]; - let col_constraint = vec![quote!(Constraint::default()); grid_layout.col_count()]; - let cells = grid_layout - .elems - .iter() - .map(|x| { - x.iter() - .map(|y| { - y.as_ref() - .map(|elem| { - let e = quote::format_ident!("{}", elem.borrow().id); - let p = |n: &str| { - if elem.borrow().lookup_property(n) == Type::Length { - let n = quote::format_ident!("{}", n); - quote! {Some(&self.#e.#n)} - } else { - quote! {None} - } - }; - let width = p("width"); - let height = p("height"); - let x = p("x"); - let y = p("y"); - quote!(GridLayoutCellData { - x: #x, - y: #y, - width: #width, - height: #height, - }) - }) - .unwrap_or_else(|| quote!(GridLayoutCellData::default())) - }) - .collect::>() + let cells = grid_layout.elems.iter().map(|cell| { + let e = quote::format_ident!("{}", cell.item.borrow().id); + let p = |n: &str| { + if cell.item.borrow().lookup_property(n) == Type::Length { + let n = quote::format_ident!("{}", n); + quote! {Some(&self.#e.#n)} + } else { + quote! {None} + } + }; + let width = p("width"); + let height = p("height"); + let x = p("x"); + let y = p("y"); + let (col, row, colspan, rowspan) = (cell.col, cell.row, cell.colspan, cell.rowspan); + quote!(GridLayoutCellData { + x: #x, + y: #y, + width: #width, + height: #height, + col: #col, + row: #row, + colspan: #colspan, + rowspan: #rowspan, + constraint: Self::field_offsets().#e.apply_pin(self).layouting_info(), }) - .collect::>(); + }); let x_pos = compile_expression(&*grid_layout.x_reference, &component); let y_pos = compile_expression(&*grid_layout.y_reference, &component); layouts.push(quote! { solve_grid_layout(&GridLayoutData { - row_constraint: Slice::from_slice(&[#(#row_constraint),*]), - col_constraint: Slice::from_slice(&[#(#col_constraint),*]), width: (Self::field_offsets().#within + #within_ty::field_offsets().width) .apply_pin(self).get(), height: (Self::field_offsets().#within + #within_ty::field_offsets().height) .apply_pin(self).get(), x: #x_pos, y: #y_pos, - cells: Slice::from_slice(&[#( Slice::from_slice(&[#( #cells ),*])),*]), + cells: Slice::from_slice(&[#( #cells ),*]), }); }); } diff --git a/sixtyfps_compiler/layout.rs b/sixtyfps_compiler/layout.rs index 14abeb574..4d64925dd 100644 --- a/sixtyfps_compiler/layout.rs +++ b/sixtyfps_compiler/layout.rs @@ -17,6 +17,16 @@ impl ExpressionFieldsVisitor for LayoutConstraints { } } +/// An element in a GridLayout +#[derive(Debug)] +pub struct GridLayoutElement { + pub col: u16, + pub row: u16, + pub colspan: u16, + pub rowspan: u16, + pub item: ElementRc, +} + /// Internal representation of a grid layout #[derive(Debug)] pub struct GridLayout { @@ -24,21 +34,11 @@ pub struct GridLayout { /// /// FIXME: This should not be implemented like that instead there should be pub within: ElementRc, - /// This is like a matrix of elements. - pub elems: Vec>>, + pub elems: Vec, pub x_reference: Box, pub y_reference: Box, } -impl GridLayout { - pub fn col_count(&self) -> usize { - self.elems.iter().map(|x| x.len()).max().unwrap_or(0) - } - pub fn row_count(&self) -> usize { - self.elems.len() - } -} - impl ExpressionFieldsVisitor for GridLayout { fn visit_expressions(&mut self, mut visitor: impl FnMut(&mut Expression)) { visitor(&mut self.x_reference); diff --git a/sixtyfps_compiler/lib.rs b/sixtyfps_compiler/lib.rs index 5d3e0347a..b6f7a1657 100644 --- a/sixtyfps_compiler/lib.rs +++ b/sixtyfps_compiler/lib.rs @@ -100,8 +100,8 @@ pub fn run_passes( passes::inlining::inline(doc); passes::compile_paths::compile_paths(&doc.root_component, &doc.local_registry, diag); passes::unique_id::assign_unique_id(&doc.root_component); - passes::materialize_fake_properties::materialize_fake_properties(&doc.root_component); passes::lower_layout::lower_layouts(&doc.root_component, diag); + passes::materialize_fake_properties::materialize_fake_properties(&doc.root_component); if compiler_config.embed_resources { passes::collect_resources::collect_resources(&doc.root_component); } diff --git a/sixtyfps_compiler/passes/lower_layout.rs b/sixtyfps_compiler/passes/lower_layout.rs index ab9cf24fb..fe9c28d67 100644 --- a/sixtyfps_compiler/passes/lower_layout.rs +++ b/sixtyfps_compiler/passes/lower_layout.rs @@ -119,25 +119,7 @@ pub fn lower_layouts(component: &Rc, diag: &mut BuildDiagnostics) { } impl GridLayout { - fn add_element( - &mut self, - elem: ElementRc, - row: usize, - col: usize, - diag: &mut BuildDiagnostics, - ) { - fn index_checked(vec: &mut Vec, idx: usize) -> &mut T { - if vec.len() <= idx { - vec.resize_with(idx + 1, T::default) - } - &mut vec[idx] - }; - - let row_vec = index_checked(&mut self.elems, row); - let cell = index_checked(row_vec, col); - if cell.is_some() { - diag.push_error(format!("Multiple elements in the same cell"), &*elem.borrow()) - } - *cell = Some(elem) + fn add_element(&mut self, item: ElementRc, row: u16, col: u16, _diag: &mut BuildDiagnostics) { + self.elems.push(GridLayoutElement { col, row, colspan: 1, rowspan: 1, item }); } } diff --git a/sixtyfps_runtime/corelib/abi/slice.rs b/sixtyfps_runtime/corelib/abi/slice.rs index 6db099475..a898ff884 100644 --- a/sixtyfps_runtime/corelib/abi/slice.rs +++ b/sixtyfps_runtime/corelib/abi/slice.rs @@ -1,6 +1,6 @@ //! FFI-friendly slice -use core::{cmp::PartialEq, marker::PhantomData, ptr::NonNull}; +use core::{cmp::PartialEq, fmt::Debug, marker::PhantomData, ptr::NonNull}; /// That's basicaly the same as `&'a [T]` but `repr(C)` /// @@ -28,7 +28,7 @@ use core::{cmp::PartialEq, marker::PhantomData, ptr::NonNull}; /// assert_eq!(a.as_slice(), b.as_slice()); /// ``` #[repr(C)] -#[derive(PartialEq, Debug)] +#[derive(PartialEq)] pub struct Slice<'a, T> { /// Invariant, this is a valid slice of len `len` ptr: NonNull, @@ -36,6 +36,12 @@ pub struct Slice<'a, T> { phantom: PhantomData<&'a [T]>, } +impl<'a, T: Debug> Debug for Slice<'a, T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.as_slice().fmt(f) + } +} + // Need to implement manually otheriwse it is not implemented if T do not implement Copy / Clone impl<'a, T> Copy for Slice<'a, T> {} diff --git a/sixtyfps_runtime/corelib/layout.rs b/sixtyfps_runtime/corelib/layout.rs index 1b7e2a62f..550c4227e 100644 --- a/sixtyfps_runtime/corelib/layout.rs +++ b/sixtyfps_runtime/corelib/layout.rs @@ -9,7 +9,7 @@ type Coord = f32; mod internal { use super::*; - #[derive(Debug, Default)] + #[derive(Debug, Clone)] pub struct LayoutData { // inputs pub min: Coord, @@ -22,6 +22,12 @@ mod internal { pub size: Coord, } + impl Default for LayoutData { + fn default() -> Self { + LayoutData { min: 0., max: Coord::MAX, pref: 0., stretch: 1., pos: 0., size: 0. } + } + } + /// Layout the items within a specified size /// /// This is quite a simple implementation for now @@ -112,19 +118,23 @@ impl Default for Constraint { } #[repr(C)] +#[derive(Debug)] pub struct GridLayoutData<'a> { - pub row_constraint: Slice<'a, Constraint>, - pub col_constraint: Slice<'a, Constraint>, pub width: Coord, pub height: Coord, pub x: Coord, pub y: Coord, - pub cells: Slice<'a, Slice<'a, GridLayoutCellData<'a>>>, + pub cells: Slice<'a, GridLayoutCellData<'a>>, } #[repr(C)] -#[derive(Default)] +#[derive(Default, Debug)] pub struct GridLayoutCellData<'a> { + pub col: u16, + pub row: u16, + pub colspan: u16, + pub rowspan: u16, + pub constraint: crate::abi::datastructures::LayoutInfo, pub x: Option<&'a Property>, pub y: Option<&'a Property>, pub width: Option<&'a Property>, @@ -134,26 +144,34 @@ pub struct GridLayoutCellData<'a> { /// FIXME: rename with sixstyfps prefix #[no_mangle] pub extern "C" fn solve_grid_layout(data: &GridLayoutData) { - let map = |c: &Constraint| internal::LayoutData { - min: c.min, - max: c.max, - pref: c.min, - stretch: 1., - pos: 0., - size: 0., - }; + let (mut num_col, mut num_row) = (0, 0); + for cell in data.cells.iter() { + num_row = num_row.max(cell.row + cell.rowspan); + num_col = num_col.max(cell.col + cell.colspan); + } + + let mut row_layout_data = vec![internal::LayoutData::default(); num_row as usize]; + let mut col_layout_data = vec![internal::LayoutData::default(); num_col as usize]; + for cell in data.cells.iter() { + let rdata = &mut row_layout_data[cell.row as usize]; + let cdata = &mut col_layout_data[cell.col as usize]; + rdata.max = rdata.max.min(cell.constraint.max_height); + cdata.max = cdata.max.min(cell.constraint.max_width); + rdata.min = rdata.min.max(cell.constraint.min_height); + cdata.min = cdata.min.max(cell.constraint.min_width); + rdata.pref = rdata.pref.max(cell.constraint.min_height); + cdata.pref = cdata.pref.max(cell.constraint.min_width); + } - let mut row_layout_data = data.row_constraint.iter().map(map).collect::>(); - let mut col_layout_data = data.col_constraint.iter().map(map).collect::>(); internal::layout_items(&mut row_layout_data, data.y, data.height); internal::layout_items(&mut col_layout_data, data.x, data.width); - for (row_data, row) in row_layout_data.iter().zip(data.cells.iter()) { - for (col_data, cell) in col_layout_data.iter().zip(row.iter()) { - cell.x.map(|p| p.set(col_data.pos)); - cell.width.map(|p| p.set(col_data.size)); - cell.y.map(|p| p.set(row_data.pos)); - cell.height.map(|p| p.set(row_data.size)); - } + for cell in data.cells.iter() { + let rdata = &row_layout_data[cell.row as usize]; + let cdata = &col_layout_data[cell.col as usize]; + cell.x.map(|p| p.set(cdata.pos)); + cell.width.map(|p| p.set(cdata.size)); + cell.y.map(|p| p.set(rdata.pos)); + cell.height.map(|p| p.set(rdata.size)); } } diff --git a/sixtyfps_runtime/interpreter/dynamic_component.rs b/sixtyfps_runtime/interpreter/dynamic_component.rs index cbaa6d035..f813cd7d6 100644 --- a/sixtyfps_runtime/interpreter/dynamic_component.rs +++ b/sixtyfps_runtime/interpreter/dynamic_component.rs @@ -629,6 +629,8 @@ pub fn instantiate( component_box } +use sixtyfps_corelib::layout::*; + unsafe extern "C" fn compute_layout(component: ComponentRefPin) { // This is fine since we can only be called with a component that with our vtable which is a ComponentDescription let component_type = @@ -641,43 +643,33 @@ unsafe extern "C" fn compute_layout(component: ComponentRefPin) { }; for it in &component_type.original.layout_constraints.borrow().grids { - use sixtyfps_corelib::layout::*; - - let mut row_constraint = vec![]; - let mut col_constraint = vec![]; - //let mut cells = vec![]; - - row_constraint.resize_with(it.row_count(), Default::default); - col_constraint.resize_with(it.col_count(), Default::default); - - let cells_v = it + let cells = it .elems .iter() - .map(|x| { - x.iter() - .map(|y| { - y.as_ref() - .map(|elem| { - let info = &component_type.items[elem.borrow().id.as_str()]; - let get_prop = |name| { - info.rtti.properties.get(name).map(|p| { - &*(component.as_ptr().add(info.offset).add(p.offset()) - as *const Property) - }) - }; - GridLayoutCellData { - x: get_prop("x"), - y: get_prop("y"), - width: get_prop("width"), - height: get_prop("height"), - } - }) - .unwrap_or_default() + .map(|cell| { + let info = &component_type.items[cell.item.borrow().id.as_str()]; + let get_prop = |name| { + info.rtti.properties.get(name).map(|p| { + &*(component.as_ptr().add(info.offset).add(p.offset()) + as *const Property) }) - .collect::>() + }; + GridLayoutCellData { + x: get_prop("x"), + y: get_prop("y"), + width: get_prop("width"), + height: get_prop("height"), + col: cell.col, + row: cell.row, + colspan: cell.colspan, + rowspan: cell.rowspan, + constraint: info + .item_from_component(component.as_ptr()) + .as_ref() + .layouting_info(), + } }) .collect::>(); - let cells = cells_v.iter().map(|x| x.as_slice().into()).collect::>>(); let within_info = &component_type.items[it.within.borrow().id.as_str()]; let within_prop = |name| { @@ -688,8 +680,6 @@ unsafe extern "C" fn compute_layout(component: ComponentRefPin) { }; solve_grid_layout(&GridLayoutData { - row_constraint: Slice::from(row_constraint.as_slice()), - col_constraint: Slice::from(col_constraint.as_slice()), width: within_prop("width"), height: within_prop("height"), x: resolve_prop_ref(&it.x_reference),