Implement support for nested layouts in the C++ generator

This commit is contained in:
Simon Hausmann 2020-07-30 16:23:48 +02:00
parent 884af3c88f
commit cb7aca54d4
2 changed files with 271 additions and 155 deletions

View file

@ -28,8 +28,8 @@ using internal::ComponentVTable;
using internal::ItemTreeNode; using internal::ItemTreeNode;
using ComponentRef = VRef<ComponentVTable>; using ComponentRef = VRef<ComponentVTable>;
using ItemVisitorRefMut = VRefMut<internal::ItemVisitorVTable>; using ItemVisitorRefMut = VRefMut<internal::ItemVisitorVTable>;
using internal::WindowProperties;
using internal::EasingCurve; using internal::EasingCurve;
using internal::WindowProperties;
struct ComponentWindow struct ComponentWindow
{ {
@ -52,12 +52,12 @@ private:
}; };
using internal::BorderRectangle; using internal::BorderRectangle;
using internal::Flickable;
using internal::Image; using internal::Image;
using internal::Path; using internal::Path;
using internal::Rectangle; using internal::Rectangle;
using internal::Text; using internal::Text;
using internal::TouchArea; using internal::TouchArea;
using internal::Flickable;
constexpr inline ItemTreeNode<uint8_t> make_item_node(std::uintptr_t offset, constexpr inline ItemTreeNode<uint8_t> make_item_node(std::uintptr_t offset,
const internal::ItemVTable *vtable, const internal::ItemVTable *vtable,
@ -76,6 +76,7 @@ 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::grid_layout_info;
using internal::GridLayoutCellData; using internal::GridLayoutCellData;
using internal::GridLayoutData; using internal::GridLayoutData;
using internal::PathLayoutData; using internal::PathLayoutData;
@ -145,7 +146,13 @@ struct Repeater
} }
}; };
Flickable::Flickable() { sixtyfps_flickable_data_init(&data); } Flickable::Flickable()
Flickable::~Flickable() { sixtyfps_flickable_data_free(&data); } {
sixtyfps_flickable_data_init(&data);
}
Flickable::~Flickable()
{
sixtyfps_flickable_data_free(&data);
}
} // namespace sixtyfps } // namespace sixtyfps

View file

@ -131,7 +131,7 @@ mod cpp_ast {
use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic, Spanned}; use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic, Spanned};
use crate::expression_tree::{EasingCurve, Expression, ExpressionSpanned}; use crate::expression_tree::{EasingCurve, Expression, ExpressionSpanned};
use crate::layout::{Layout, LayoutItem}; use crate::layout::{GridLayout, Layout, LayoutItem, PathLayout};
use crate::object_tree::{Component, Element, ElementRc, RepeatedElementInfo}; use crate::object_tree::{Component, Element, ElementRc, RepeatedElementInfo};
use crate::typeregister::Type; use crate::typeregister::Type;
use cpp_ast::*; use cpp_ast::*;
@ -891,9 +891,41 @@ fn compile_expression(e: &crate::expression_tree::Expression, component: &Rc<Com
} }
} }
#[derive(derive_more::From)]
pub enum PendingLayout<'a> {
GridLayout(&'a GridLayout),
PathLayout(&'a PathLayout),
}
pub struct GridLayoutWithCells<'a> {
grid: &'a GridLayout,
cell_creation_code: String,
cell_ref_variable: String,
}
#[derive(derive_more::From)]
enum LayoutTreeItem<'a> {
GridLayout(GridLayoutWithCells<'a>),
PathLayout(&'a PathLayout),
}
impl<'a> LayoutTreeItem<'a> {
fn layout_info(&self) -> String {
match self {
LayoutTreeItem::GridLayout(grid_layout) => {
format!("sixtyfps::grid_layout_info(&{cv})", cv = grid_layout.cell_ref_variable)
}
LayoutTreeItem::PathLayout(_) => todo!(),
}
}
}
trait LayoutItemCodeGen { trait LayoutItemCodeGen {
fn get_property_ref(&self, name: &str) -> String; fn get_property_ref(&self, name: &str) -> String;
fn get_layout_info_ref(&self) -> String; fn get_layout_info_ref<'a, 'b>(
&'a self,
layout_tree: &'b mut Vec<LayoutTreeItem<'a>>,
) -> String;
} }
impl LayoutItemCodeGen for LayoutItem { impl LayoutItemCodeGen for LayoutItem {
@ -903,20 +935,31 @@ impl LayoutItemCodeGen for LayoutItem {
LayoutItem::Layout(l) => l.get_property_ref(name), LayoutItem::Layout(l) => l.get_property_ref(name),
} }
} }
fn get_layout_info_ref(&self) -> String { fn get_layout_info_ref<'a, 'b>(
&'a self,
layout_tree: &'b mut Vec<LayoutTreeItem<'a>>,
) -> String {
match self { match self {
LayoutItem::Element(e) => e.get_layout_info_ref(), LayoutItem::Element(e) => e.get_layout_info_ref(layout_tree),
LayoutItem::Layout(l) => l.get_layout_info_ref(), LayoutItem::Layout(l) => l.get_layout_info_ref(layout_tree),
} }
} }
} }
impl LayoutItemCodeGen for Layout { impl LayoutItemCodeGen for Layout {
fn get_property_ref(&self, _name: &str) -> String { fn get_property_ref(&self, name: &str) -> String {
todo!() let moved_property_name = match self.rect().mapped_property_name(name) {
Some(name) => name,
None => return "nullptr".to_owned(),
};
format!("&self->{}", moved_property_name)
} }
fn get_layout_info_ref(&self) -> String { fn get_layout_info_ref<'a, 'b>(
todo!() &'a self,
layout_tree: &'b mut Vec<LayoutTreeItem<'a>>,
) -> String {
let self_as_layout_tree_item = collect_layouts_recursively(layout_tree, &self);
self_as_layout_tree_item.layout_info()
} }
} }
@ -928,7 +971,10 @@ impl LayoutItemCodeGen for ElementRc {
"nullptr".to_owned() "nullptr".to_owned()
} }
} }
fn get_layout_info_ref(&self) -> String { fn get_layout_info_ref<'a, 'b>(
&'a self,
_layout_tree: &'b mut Vec<LayoutTreeItem<'a>>,
) -> String {
format!( format!(
"sixtyfps::{vt}.layouting_info({{&sixtyfps::{vt}, const_cast<sixtyfps::{ty}*>(&self->{id})}})", "sixtyfps::{vt}.layouting_info({{&sixtyfps::{vt}, const_cast<sixtyfps::{ty}*>(&self->{id})}})",
vt = self.borrow().base_type.as_native().vtable_symbol, vt = self.borrow().base_type.as_native().vtable_symbol,
@ -938,55 +984,89 @@ impl LayoutItemCodeGen for ElementRc {
} }
} }
fn compute_layout(component: &Rc<Component>) -> Vec<String> { fn collect_layouts_recursively<'a, 'b>(
let mut res = vec![]; layout_tree: &'b mut Vec<LayoutTreeItem<'a>>,
layout: &'a Layout,
res.push(format!( ) -> &'b LayoutTreeItem<'a> {
"[[maybe_unused]] auto self = reinterpret_cast<const {ty}*>(component.instance);", match layout {
ty = component_id(component) Layout::GridLayout(grid_layout) => {
)); let mut cell_creation_code = Vec::new();
component.layout_constraints.borrow().iter().for_each(|layout| match &layout { for cell in &grid_layout.elems {
Layout::GridLayout(grid) => { cell_creation_code.push(format!(
res.push("{".to_owned());
res.push(" sixtyfps::GridLayoutCellData grid_data[] = {".to_owned());
for cell in &grid.elems {
res.push(format!(
" {{ {c}, {r}, {cs}, {rs}, {li}, {x}, {y}, {w}, {h} }},", " {{ {c}, {r}, {cs}, {rs}, {li}, {x}, {y}, {w}, {h} }},",
c = cell.col, c = cell.col,
r = cell.row, r = cell.row,
cs = cell.colspan, cs = cell.colspan,
rs = cell.rowspan, rs = cell.rowspan,
li = cell.item.get_layout_info_ref(), li = cell.item.get_layout_info_ref(layout_tree),
x = cell.item.get_property_ref("x"), x = cell.item.get_property_ref("x"),
y = cell.item.get_property_ref("y"), y = cell.item.get_property_ref("y"),
w = cell.item.get_property_ref("width"), w = cell.item.get_property_ref("width"),
h = cell.item.get_property_ref("height") h = cell.item.get_property_ref("height")
)); ));
} }
res.push(" };".to_owned()); let cell_ref_variable = format!("cells_{}", layout_tree.len()).to_owned();
res.push(format!( cell_creation_code.insert(
" auto width = {};", 0,
compile_expression(&grid.rect.width_reference, component) format!(" sixtyfps::GridLayoutCellData {}_data[] = {{", cell_ref_variable,),
);
cell_creation_code.push(" };".to_owned());
cell_creation_code.push(format!(
" const sixtyfps::Slice<sixtyfps::GridLayoutCellData> {cv}{{{cv}_data, std::size({cv}_data)}};",
cv = cell_ref_variable
)); ));
res.push(format!(
" auto height = {};",
compile_expression(&grid.rect.height_reference, component)
));
res.push(" sixtyfps::GridLayoutData grid { ".into());
res.push(format!(
" width, height, {}, {},",
compile_expression(&grid.rect.x_reference, component),
compile_expression(&grid.rect.y_reference, component)
));
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());
}
Layout::PathLayout(path_layout) => {
res.push("{".to_owned());
let path_layout_item_data = |elem: &ElementRc, elem_cpp: &str, component_cpp: &str| { layout_tree.push(
GridLayoutWithCells {
grid: grid_layout,
cell_creation_code: cell_creation_code.join("\n"),
cell_ref_variable,
}
.into(),
)
}
Layout::PathLayout(path_layout) => layout_tree.push(path_layout.into()),
}
layout_tree.last().unwrap()
}
impl<'a> LayoutTreeItem<'a> {
fn layout_info_collecting_code(&self) -> Option<String> {
match self {
LayoutTreeItem::GridLayout(grid_layout) => Some(grid_layout.cell_creation_code.clone()),
LayoutTreeItem::PathLayout(_) => None,
}
}
fn emit_solve_calls(&self, component: &Rc<Component>, code_stream: &mut Vec<String>) {
match self {
LayoutTreeItem::GridLayout(grid_layout) => {
code_stream.push(" { ".into());
code_stream.push(format!(
" auto width = {};",
compile_expression(&grid_layout.grid.rect.width_reference, component)
));
code_stream.push(format!(
" auto height = {};",
compile_expression(&grid_layout.grid.rect.height_reference, component)
));
code_stream.push(" sixtyfps::GridLayoutData grid { ".into());
code_stream.push(format!(
" width, height, {}, {},",
compile_expression(&grid_layout.grid.rect.x_reference, component),
compile_expression(&grid_layout.grid.rect.y_reference, component)
));
code_stream
.push(format!(" {cv}", cv = grid_layout.cell_ref_variable).to_owned());
code_stream.push(" };".to_owned());
code_stream.push(" sixtyfps::solve_grid_layout(&grid);".to_owned());
code_stream.push(" } ".into());
}
LayoutTreeItem::PathLayout(path_layout) => {
code_stream.push("{".to_owned());
let path_layout_item_data =
|elem: &ElementRc, elem_cpp: &str, component_cpp: &str| {
let prop_ref = |n: &str| { let prop_ref = |n: &str| {
if elem.borrow().lookup_property(n) == Type::Length { if elem.borrow().lookup_property(n) == Type::Length {
format!("&{}.{}", elem_cpp, n) format!("&{}.{}", elem_cpp, n)
@ -1023,23 +1103,25 @@ fn compute_layout(component: &Rc<Component>) -> Vec<String> {
path_layout.elements.iter().all(|elem| elem.borrow().repeated.is_none()); path_layout.elements.iter().all(|elem| elem.borrow().repeated.is_none());
let slice = if is_static_array { let slice = if is_static_array {
res.push(" sixtyfps::PathLayoutItemData items[] = {".to_owned()); code_stream.push(" sixtyfps::PathLayoutItemData items[] = {".to_owned());
for elem in &path_layout.elements { for elem in &path_layout.elements {
res.push(format!(" {},", path_layout_item_data_for_elem(elem))); code_stream
.push(format!(" {},", path_layout_item_data_for_elem(elem)));
} }
res.push(" };".to_owned()); code_stream.push(" };".to_owned());
" {items, std::size(items)},".to_owned() " {items, std::size(items)},".to_owned()
} else { } else {
res.push(" std::vector<sixtyfps::PathLayoutItemData> items;".to_owned()); code_stream
.push(" std::vector<sixtyfps::PathLayoutItemData> items;".to_owned());
for elem in &path_layout.elements { for elem in &path_layout.elements {
if elem.borrow().repeated.is_some() { if elem.borrow().repeated.is_some() {
let root_element = let root_element =
elem.borrow().base_type.as_component().root_element.clone(); elem.borrow().base_type.as_component().root_element.clone();
res.push(format!( code_stream.push(format!(
" for (auto &&sub_comp : self->repeater_{}.data)", " for (auto &&sub_comp : self->repeater_{}.data)",
elem.borrow().id elem.borrow().id
)); ));
res.push(format!( code_stream.push(format!(
" items.push_back({});", " items.push_back({});",
path_layout_item_data( path_layout_item_data(
&root_element, &root_element,
@ -1048,7 +1130,7 @@ fn compute_layout(component: &Rc<Component>) -> Vec<String> {
) )
)); ));
} else { } else {
res.push(format!( code_stream.push(format!(
" items.push_back({});", " items.push_back({});",
path_layout_item_data_for_elem(elem) path_layout_item_data_for_elem(elem)
)); ));
@ -1057,38 +1139,65 @@ fn compute_layout(component: &Rc<Component>) -> Vec<String> {
" {items.data(), std::size(items)},".to_owned() " {items.data(), std::size(items)},".to_owned()
}; };
res.push(format!(" auto path = {};", compile_path(&path_layout.path, component))); code_stream.push(format!(
" auto path = {};",
compile_path(&path_layout.path, component)
));
res.push(format!( code_stream.push(format!(
" auto x = {};", " auto x = {};",
compile_expression(&path_layout.rect.x_reference, component) compile_expression(&path_layout.rect.x_reference, component)
)); ));
res.push(format!( code_stream.push(format!(
" auto y = {};", " auto y = {};",
compile_expression(&path_layout.rect.y_reference, component) compile_expression(&path_layout.rect.y_reference, component)
)); ));
res.push(format!( code_stream.push(format!(
" auto width = {};", " auto width = {};",
compile_expression(&path_layout.rect.width_reference, component) compile_expression(&path_layout.rect.width_reference, component)
)); ));
res.push(format!( code_stream.push(format!(
" auto height = {};", " auto height = {};",
compile_expression(&path_layout.rect.height_reference, component) compile_expression(&path_layout.rect.height_reference, component)
)); ));
res.push(format!( code_stream.push(format!(
" auto offset = {};", " auto offset = {};",
compile_expression(&path_layout.offset_reference, component) compile_expression(&path_layout.offset_reference, component)
)); ));
res.push(" sixtyfps::PathLayoutData pl { ".into()); code_stream.push(" sixtyfps::PathLayoutData pl { ".into());
res.push(" &path,".to_owned()); code_stream.push(" &path,".to_owned());
res.push(slice); code_stream.push(slice);
res.push(" x, y, width, height, offset".to_owned()); code_stream.push(" x, y, width, height, offset".to_owned());
res.push(" };".to_owned()); code_stream.push(" };".to_owned());
res.push(" sixtyfps::solve_path_layout(&pl);".to_owned()); code_stream.push(" sixtyfps::solve_path_layout(&pl);".to_owned());
res.push("}".to_owned()); code_stream.push("}".to_owned());
} }
}
}
}
fn compute_layout(component: &Rc<Component>) -> Vec<String> {
let mut res = vec![];
res.push(format!(
"[[maybe_unused]] auto self = reinterpret_cast<const {ty}*>(component.instance);",
ty = component_id(component)
));
component.layout_constraints.borrow().iter().for_each(|layout| {
let mut inverse_layout_tree = Vec::new();
collect_layouts_recursively(&mut inverse_layout_tree, layout);
res.extend(
inverse_layout_tree.iter().filter_map(|layout| layout.layout_info_collecting_code()),
);
inverse_layout_tree
.iter()
.rev()
.for_each(|layout| layout.emit_solve_calls(component, &mut res));
}); });
res res