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;
// layouts:
using internal::Constraint;
using internal::GridLayoutCellData;
using internal::GridLayoutData;
using internal::PathLayoutData;

View file

@ -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;

View file

@ -892,56 +892,32 @@ fn compute_layout(component: &Rc<Component>) -> Vec<String> {
));
for grid in component.layout_constraints.borrow().grids.iter() {
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());
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 {
for cell in &grid.elems {
let p = |n: &str| {
if cell.borrow().lookup_property(n) == Type::Length {
format!("&self->{}.{}", cell.borrow().id, n)
if cell.item.borrow().lookup_property(n) == Type::Length {
format!("&self->{}.{}", cell.item.borrow().id, n)
} else {
"nullptr".to_owned()
}
};
res.push(format!(
" {{ {}, {}, {}, {} }},",
p("x"),
p("y"),
p("width"),
p("height")
" {{ {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")
));
} else {
res.push(" {},".into());
}
count += 1;
}
}
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 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());

View file

@ -720,19 +720,10 @@ fn compute_layout(component: &Rc<Component>) -> 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 cells = grid_layout.elems.iter().map(|cell| {
let e = quote::format_ident!("{}", cell.item.borrow().id);
let p = |n: &str| {
if elem.borrow().lookup_property(n) == Type::Length {
if cell.item.borrow().lookup_property(n) == Type::Length {
let n = quote::format_ident!("{}", n);
quote! {Some(&self.#e.#n)}
} else {
@ -743,33 +734,32 @@ fn compute_layout(component: &Rc<Component>) -> TokenStream {
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(),
})
})
.unwrap_or_else(|| quote!(GridLayoutCellData::default()))
})
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
});
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 ),*]),
});
});
}

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
#[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<Vec<Option<ElementRc>>>,
pub elems: Vec<GridLayoutElement>,
pub x_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 {
fn visit_expressions(&mut self, mut visitor: impl FnMut(&mut Expression)) {
visitor(&mut self.x_reference);

View file

@ -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);
}

View file

@ -119,25 +119,7 @@ pub fn lower_layouts(component: &Rc<Component>, diag: &mut BuildDiagnostics) {
}
impl GridLayout {
fn add_element(
&mut self,
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)
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 });
}
}

View file

@ -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<T>,
@ -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> {}

View file

@ -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<Coord>>,
pub y: Option<&'a Property<Coord>>,
pub width: Option<&'a Property<Coord>>,
@ -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::<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 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));
}
}

View file

@ -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,24 +643,11 @@ 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()];
.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())
@ -670,14 +659,17 @@ unsafe extern "C" fn compute_layout(component: ComponentRefPin) {
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(),
}
})
.unwrap_or_default()
})
.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_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),