Refactor GridLayout

This commit is contained in:
Olivier Goffart 2020-07-28 10:29:48 +02:00
parent 0a722c86b3
commit b57f3775c9
10 changed files with 130 additions and 169 deletions

View file

@ -73,7 +73,6 @@ constexpr inline ItemTreeNode<uint8_t> make_dyn_node(std::uintptr_t offset)
using internal::sixtyfps_visit_item_tree; using internal::sixtyfps_visit_item_tree;
// layouts: // layouts:
using internal::Constraint;
using internal::GridLayoutCellData; using internal::GridLayoutCellData;
using internal::GridLayoutData; using internal::GridLayoutData;
using internal::PathLayoutData; using internal::PathLayoutData;

View file

@ -81,8 +81,8 @@ pub mod re_exports {
pub use sixtyfps_corelib::abi::slice::Slice; pub use sixtyfps_corelib::abi::slice::Slice;
pub use sixtyfps_corelib::item_tree::visit_item_tree; pub use sixtyfps_corelib::item_tree::visit_item_tree;
pub use sixtyfps_corelib::layout::{ pub use sixtyfps_corelib::layout::{
solve_grid_layout, solve_path_layout, Constraint, GridLayoutCellData, GridLayoutData, solve_grid_layout, solve_path_layout, GridLayoutCellData, GridLayoutData, PathLayoutData,
PathLayoutData, PathLayoutItemData, PathLayoutItemData,
}; };
pub use sixtyfps_corelib::Color; pub use sixtyfps_corelib::Color;
pub use sixtyfps_corelib::ComponentVTable_static; pub use sixtyfps_corelib::ComponentVTable_static;

View file

@ -892,56 +892,32 @@ fn compute_layout(component: &Rc<Component>) -> Vec<String> {
)); ));
for grid in component.layout_constraints.borrow().grids.iter() { for grid in component.layout_constraints.borrow().grids.iter() {
res.push("{".to_owned()); res.push("{".to_owned());
res.push(format!(" std::array<sixtyfps::Constraint, {}> row_constr;", grid.row_count()));
res.push(format!(" std::array<sixtyfps::Constraint, {}> col_constr;", grid.col_count()));
res.push(
" row_constr.fill(sixtyfps::Constraint{0., std::numeric_limits<float>::max()});"
.to_owned(),
);
res.push(
" col_constr.fill(sixtyfps::Constraint{0., std::numeric_limits<float>::max()});"
.to_owned(),
);
res.push(" sixtyfps::GridLayoutCellData grid_data[] = {".to_owned()); res.push(" sixtyfps::GridLayoutCellData grid_data[] = {".to_owned());
let mut row_info = vec![]; for cell in &grid.elems {
let mut count = 0; let p = |n: &str| {
for row in &grid.elems { if cell.item.borrow().lookup_property(n) == Type::Length {
row_info.push(format!("{{ &grid_data[{}], {} }}", count, row.len())); format!("&self->{}.{}", cell.item.borrow().id, n)
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")
));
} else { } else {
res.push(" {},".into()); "nullptr".to_owned()
} }
count += 1; };
} res.push(format!(
" {{ {c}, {r}, {cs}, {rs}, sixtyfps::{vt}.layouting_info({{&sixtyfps::{vt}, const_cast<sixtyfps::{ty}*>(&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(" };".to_owned());
res.push(" sixtyfps::Slice<sixtyfps::GridLayoutCellData> cells[] = {".to_owned());
res.push(format!(" {} }};", row_info.join(", ")));
res.push(format!(" auto x = {};", compile_expression(&grid.x_reference, component))); res.push(format!(" auto x = {};", compile_expression(&grid.x_reference, component)));
res.push(format!(" auto y = {};", compile_expression(&grid.y_reference, component))); res.push(format!(" auto y = {};", compile_expression(&grid.y_reference, component)));
res.push(" sixtyfps::GridLayoutData grid { ".into()); 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->{}.width.get(),", grid.within.borrow().id));
res.push(format!(" self->{}.height.get(),", grid.within.borrow().id)); res.push(format!(" self->{}.height.get(),", grid.within.borrow().id));
res.push(" x, y,".to_owned()); 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(" };".to_owned());
res.push(" sixtyfps::solve_grid_layout(&grid);".to_owned()); res.push(" sixtyfps::solve_grid_layout(&grid);".to_owned());
res.push("}".to_owned()); res.push("}".to_owned());

View file

@ -720,56 +720,46 @@ fn compute_layout(component: &Rc<Component>) -> TokenStream {
"{}", "{}",
grid_layout.within.borrow().base_type.as_native().class_name grid_layout.within.borrow().base_type.as_native().class_name
); );
let row_constraint = vec![quote!(Constraint::default()); grid_layout.row_count()]; let cells = grid_layout.elems.iter().map(|cell| {
let col_constraint = vec![quote!(Constraint::default()); grid_layout.col_count()]; let e = quote::format_ident!("{}", cell.item.borrow().id);
let cells = grid_layout let p = |n: &str| {
.elems if cell.item.borrow().lookup_property(n) == Type::Length {
.iter() let n = quote::format_ident!("{}", n);
.map(|x| { quote! {Some(&self.#e.#n)}
x.iter() } else {
.map(|y| { quote! {None}
y.as_ref() }
.map(|elem| { };
let e = quote::format_ident!("{}", elem.borrow().id); let width = p("width");
let p = |n: &str| { let height = p("height");
if elem.borrow().lookup_property(n) == Type::Length { let x = p("x");
let n = quote::format_ident!("{}", n); let y = p("y");
quote! {Some(&self.#e.#n)} let (col, row, colspan, rowspan) = (cell.col, cell.row, cell.colspan, cell.rowspan);
} else { quote!(GridLayoutCellData {
quote! {None} x: #x,
} y: #y,
}; width: #width,
let width = p("width"); height: #height,
let height = p("height"); col: #col,
let x = p("x"); row: #row,
let y = p("y"); colspan: #colspan,
quote!(GridLayoutCellData { rowspan: #rowspan,
x: #x, constraint: Self::field_offsets().#e.apply_pin(self).layouting_info(),
y: #y,
width: #width,
height: #height,
})
})
.unwrap_or_else(|| quote!(GridLayoutCellData::default()))
})
.collect::<Vec<_>>()
}) })
.collect::<Vec<_>>(); });
let x_pos = compile_expression(&*grid_layout.x_reference, &component); let x_pos = compile_expression(&*grid_layout.x_reference, &component);
let y_pos = compile_expression(&*grid_layout.y_reference, &component); let y_pos = compile_expression(&*grid_layout.y_reference, &component);
layouts.push(quote! { layouts.push(quote! {
solve_grid_layout(&GridLayoutData { 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) width: (Self::field_offsets().#within + #within_ty::field_offsets().width)
.apply_pin(self).get(), .apply_pin(self).get(),
height: (Self::field_offsets().#within + #within_ty::field_offsets().height) height: (Self::field_offsets().#within + #within_ty::field_offsets().height)
.apply_pin(self).get(), .apply_pin(self).get(),
x: #x_pos, x: #x_pos,
y: #y_pos, y: #y_pos,
cells: Slice::from_slice(&[#( Slice::from_slice(&[#( #cells ),*])),*]), cells: Slice::from_slice(&[#( #cells ),*]),
}); });
}); });
} }

View file

@ -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 /// Internal representation of a grid layout
#[derive(Debug)] #[derive(Debug)]
pub struct GridLayout { pub struct GridLayout {
@ -24,21 +34,11 @@ pub struct GridLayout {
/// ///
/// FIXME: This should not be implemented like that instead there should be /// FIXME: This should not be implemented like that instead there should be
pub within: ElementRc, pub within: ElementRc,
/// This is like a matrix of elements. pub elems: Vec<GridLayoutElement>,
pub elems: Vec<Vec<Option<ElementRc>>>,
pub x_reference: Box<Expression>, pub x_reference: Box<Expression>,
pub y_reference: Box<Expression>, pub y_reference: Box<Expression>,
} }
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 { impl ExpressionFieldsVisitor for GridLayout {
fn visit_expressions(&mut self, mut visitor: impl FnMut(&mut Expression)) { fn visit_expressions(&mut self, mut visitor: impl FnMut(&mut Expression)) {
visitor(&mut self.x_reference); visitor(&mut self.x_reference);

View file

@ -100,8 +100,8 @@ pub fn run_passes(
passes::inlining::inline(doc); passes::inlining::inline(doc);
passes::compile_paths::compile_paths(&doc.root_component, &doc.local_registry, diag); passes::compile_paths::compile_paths(&doc.root_component, &doc.local_registry, diag);
passes::unique_id::assign_unique_id(&doc.root_component); 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::lower_layout::lower_layouts(&doc.root_component, diag);
passes::materialize_fake_properties::materialize_fake_properties(&doc.root_component);
if compiler_config.embed_resources { if compiler_config.embed_resources {
passes::collect_resources::collect_resources(&doc.root_component); passes::collect_resources::collect_resources(&doc.root_component);
} }

View file

@ -119,25 +119,7 @@ pub fn lower_layouts(component: &Rc<Component>, diag: &mut BuildDiagnostics) {
} }
impl GridLayout { impl GridLayout {
fn add_element( fn add_element(&mut self, item: ElementRc, row: u16, col: u16, _diag: &mut BuildDiagnostics) {
&mut self, self.elems.push(GridLayoutElement { col, row, colspan: 1, rowspan: 1, item });
elem: ElementRc,
row: usize,
col: usize,
diag: &mut BuildDiagnostics,
) {
fn index_checked<T: Default>(vec: &mut Vec<T>, 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)
} }
} }

View file

@ -1,6 +1,6 @@
//! FFI-friendly slice //! 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)` /// 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()); /// assert_eq!(a.as_slice(), b.as_slice());
/// ``` /// ```
#[repr(C)] #[repr(C)]
#[derive(PartialEq, Debug)] #[derive(PartialEq)]
pub struct Slice<'a, T> { pub struct Slice<'a, T> {
/// Invariant, this is a valid slice of len `len` /// Invariant, this is a valid slice of len `len`
ptr: NonNull<T>, ptr: NonNull<T>,
@ -36,6 +36,12 @@ pub struct Slice<'a, T> {
phantom: PhantomData<&'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 // Need to implement manually otheriwse it is not implemented if T do not implement Copy / Clone
impl<'a, T> Copy for Slice<'a, T> {} impl<'a, T> Copy for Slice<'a, T> {}

View file

@ -9,7 +9,7 @@ type Coord = f32;
mod internal { mod internal {
use super::*; use super::*;
#[derive(Debug, Default)] #[derive(Debug, Clone)]
pub struct LayoutData { pub struct LayoutData {
// inputs // inputs
pub min: Coord, pub min: Coord,
@ -22,6 +22,12 @@ mod internal {
pub size: Coord, 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 /// Layout the items within a specified size
/// ///
/// This is quite a simple implementation for now /// This is quite a simple implementation for now
@ -112,19 +118,23 @@ impl Default for Constraint {
} }
#[repr(C)] #[repr(C)]
#[derive(Debug)]
pub struct GridLayoutData<'a> { pub struct GridLayoutData<'a> {
pub row_constraint: Slice<'a, Constraint>,
pub col_constraint: Slice<'a, Constraint>,
pub width: Coord, pub width: Coord,
pub height: Coord, pub height: Coord,
pub x: Coord, pub x: Coord,
pub y: Coord, pub y: Coord,
pub cells: Slice<'a, Slice<'a, GridLayoutCellData<'a>>>, pub cells: Slice<'a, GridLayoutCellData<'a>>,
} }
#[repr(C)] #[repr(C)]
#[derive(Default)] #[derive(Default, Debug)]
pub struct GridLayoutCellData<'a> { 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<Coord>>, pub x: Option<&'a Property<Coord>>,
pub y: Option<&'a Property<Coord>>, pub y: Option<&'a Property<Coord>>,
pub width: Option<&'a Property<Coord>>, pub width: Option<&'a Property<Coord>>,
@ -134,26 +144,34 @@ pub struct GridLayoutCellData<'a> {
/// FIXME: rename with sixstyfps prefix /// FIXME: rename with sixstyfps prefix
#[no_mangle] #[no_mangle]
pub extern "C" fn solve_grid_layout(data: &GridLayoutData) { pub extern "C" fn solve_grid_layout(data: &GridLayoutData) {
let map = |c: &Constraint| internal::LayoutData { let (mut num_col, mut num_row) = (0, 0);
min: c.min, for cell in data.cells.iter() {
max: c.max, num_row = num_row.max(cell.row + cell.rowspan);
pref: c.min, num_col = num_col.max(cell.col + cell.colspan);
stretch: 1., }
pos: 0.,
size: 0., 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::<Vec<_>>();
let mut col_layout_data = data.col_constraint.iter().map(map).collect::<Vec<_>>();
internal::layout_items(&mut row_layout_data, data.y, data.height); internal::layout_items(&mut row_layout_data, data.y, data.height);
internal::layout_items(&mut col_layout_data, data.x, data.width); 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 cell in data.cells.iter() {
for (col_data, cell) in col_layout_data.iter().zip(row.iter()) { let rdata = &row_layout_data[cell.row as usize];
cell.x.map(|p| p.set(col_data.pos)); let cdata = &col_layout_data[cell.col as usize];
cell.width.map(|p| p.set(col_data.size)); cell.x.map(|p| p.set(cdata.pos));
cell.y.map(|p| p.set(row_data.pos)); cell.width.map(|p| p.set(cdata.size));
cell.height.map(|p| p.set(row_data.size)); cell.y.map(|p| p.set(rdata.pos));
} cell.height.map(|p| p.set(rdata.size));
} }
} }

View file

@ -629,6 +629,8 @@ pub fn instantiate(
component_box component_box
} }
use sixtyfps_corelib::layout::*;
unsafe extern "C" fn compute_layout(component: ComponentRefPin) { 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 // This is fine since we can only be called with a component that with our vtable which is a ComponentDescription
let component_type = 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 { for it in &component_type.original.layout_constraints.borrow().grids {
use sixtyfps_corelib::layout::*; let cells = it
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
.elems .elems
.iter() .iter()
.map(|x| { .map(|cell| {
x.iter() let info = &component_type.items[cell.item.borrow().id.as_str()];
.map(|y| { let get_prop = |name| {
y.as_ref() info.rtti.properties.get(name).map(|p| {
.map(|elem| { &*(component.as_ptr().add(info.offset).add(p.offset())
let info = &component_type.items[elem.borrow().id.as_str()]; as *const Property<f32>)
let get_prop = |name| {
info.rtti.properties.get(name).map(|p| {
&*(component.as_ptr().add(info.offset).add(p.offset())
as *const Property<f32>)
})
};
GridLayoutCellData {
x: get_prop("x"),
y: get_prop("y"),
width: get_prop("width"),
height: get_prop("height"),
}
})
.unwrap_or_default()
}) })
.collect::<Vec<_>>() };
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::<Vec<_>>(); .collect::<Vec<_>>();
let cells = cells_v.iter().map(|x| x.as_slice().into()).collect::<Vec<Slice<_>>>();
let within_info = &component_type.items[it.within.borrow().id.as_str()]; let within_info = &component_type.items[it.within.borrow().id.as_str()];
let within_prop = |name| { let within_prop = |name| {
@ -688,8 +680,6 @@ unsafe extern "C" fn compute_layout(component: ComponentRefPin) {
}; };
solve_grid_layout(&GridLayoutData { solve_grid_layout(&GridLayoutData {
row_constraint: Slice::from(row_constraint.as_slice()),
col_constraint: Slice::from(col_constraint.as_slice()),
width: within_prop("width"), width: within_prop("width"),
height: within_prop("height"), height: within_prop("height"),
x: resolve_prop_ref(&it.x_reference), x: resolve_prop_ref(&it.x_reference),