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

View file

@ -131,7 +131,7 @@ mod cpp_ast {
use crate::diagnostics::{BuildDiagnostics, CompilerDiagnostic, Spanned};
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::typeregister::Type;
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 {
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 {
@ -903,20 +935,31 @@ impl LayoutItemCodeGen for LayoutItem {
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 {
LayoutItem::Element(e) => e.get_layout_info_ref(),
LayoutItem::Layout(l) => l.get_layout_info_ref(),
LayoutItem::Element(e) => e.get_layout_info_ref(layout_tree),
LayoutItem::Layout(l) => l.get_layout_info_ref(layout_tree),
}
}
}
impl LayoutItemCodeGen for Layout {
fn get_property_ref(&self, _name: &str) -> String {
todo!()
fn get_property_ref(&self, name: &str) -> String {
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 {
todo!()
fn get_layout_info_ref<'a, 'b>(
&'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()
}
}
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!(
"sixtyfps::{vt}.layouting_info({{&sixtyfps::{vt}, const_cast<sixtyfps::{ty}*>(&self->{id})}})",
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> {
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| match &layout {
Layout::GridLayout(grid) => {
res.push("{".to_owned());
res.push(" sixtyfps::GridLayoutCellData grid_data[] = {".to_owned());
for cell in &grid.elems {
res.push(format!(
fn collect_layouts_recursively<'a, 'b>(
layout_tree: &'b mut Vec<LayoutTreeItem<'a>>,
layout: &'a Layout,
) -> &'b LayoutTreeItem<'a> {
match layout {
Layout::GridLayout(grid_layout) => {
let mut cell_creation_code = Vec::new();
for cell in &grid_layout.elems {
cell_creation_code.push(format!(
" {{ {c}, {r}, {cs}, {rs}, {li}, {x}, {y}, {w}, {h} }},",
c = cell.col,
r = cell.row,
cs = cell.colspan,
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"),
y = cell.item.get_property_ref("y"),
w = cell.item.get_property_ref("width"),
h = cell.item.get_property_ref("height")
));
}
res.push(" };".to_owned());
res.push(format!(
" auto width = {};",
compile_expression(&grid.rect.width_reference, component)
let cell_ref_variable = format!("cells_{}", layout_tree.len()).to_owned();
cell_creation_code.insert(
0,
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| {
if elem.borrow().lookup_property(n) == Type::Length {
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());
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 {
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()
} 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 {
if elem.borrow().repeated.is_some() {
let root_element =
elem.borrow().base_type.as_component().root_element.clone();
res.push(format!(
code_stream.push(format!(
" for (auto &&sub_comp : self->repeater_{}.data)",
elem.borrow().id
));
res.push(format!(
code_stream.push(format!(
" items.push_back({});",
path_layout_item_data(
&root_element,
@ -1048,7 +1130,7 @@ fn compute_layout(component: &Rc<Component>) -> Vec<String> {
)
));
} else {
res.push(format!(
code_stream.push(format!(
" items.push_back({});",
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()
};
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 = {};",
compile_expression(&path_layout.rect.x_reference, component)
));
res.push(format!(
code_stream.push(format!(
" auto y = {};",
compile_expression(&path_layout.rect.y_reference, component)
));
res.push(format!(
code_stream.push(format!(
" auto width = {};",
compile_expression(&path_layout.rect.width_reference, component)
));
res.push(format!(
code_stream.push(format!(
" auto height = {};",
compile_expression(&path_layout.rect.height_reference, component)
));
res.push(format!(
code_stream.push(format!(
" auto offset = {};",
compile_expression(&path_layout.offset_reference, component)
));
res.push(" sixtyfps::PathLayoutData pl { ".into());
res.push(" &path,".to_owned());
res.push(slice);
res.push(" x, y, width, height, offset".to_owned());
res.push(" };".to_owned());
res.push(" sixtyfps::solve_path_layout(&pl);".to_owned());
res.push("}".to_owned());
code_stream.push(" sixtyfps::PathLayoutData pl { ".into());
code_stream.push(" &path,".to_owned());
code_stream.push(slice);
code_stream.push(" x, y, width, height, offset".to_owned());
code_stream.push(" };".to_owned());
code_stream.push(" sixtyfps::solve_path_layout(&pl);".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