diff --git a/api/sixtyfps-cpp/include/sixtyfps.h b/api/sixtyfps-cpp/include/sixtyfps.h index 7bc421da7..2d4ffa31d 100644 --- a/api/sixtyfps-cpp/include/sixtyfps.h +++ b/api/sixtyfps-cpp/include/sixtyfps.h @@ -307,10 +307,12 @@ using cbindgen_private::GridLayoutCellData; using cbindgen_private::GridLayoutData; using cbindgen_private::LayoutAlignment; using cbindgen_private::LayoutInfo; +using cbindgen_private::Orientation; using cbindgen_private::Padding; using cbindgen_private::PathLayoutData; using cbindgen_private::Rect; using cbindgen_private::sixtyfps_box_layout_info; +using cbindgen_private::sixtyfps_box_layout_info_ortho; using cbindgen_private::sixtyfps_grid_layout_info; using cbindgen_private::sixtyfps_solve_box_layout; using cbindgen_private::sixtyfps_solve_grid_layout; @@ -329,38 +331,25 @@ inline LayoutInfo LayoutInfo::merge(const LayoutInfo &other) const return (left_size + right_size) / 2.; } }; - return LayoutInfo { std::max(min_width, other.min_width), - std::min(max_width, other.max_width), - std::max(min_height, other.min_height), - std::min(max_height, other.max_height), - std::max(min_width_percent, other.min_width_percent), - std::min(max_width_percent, other.max_width_percent), - std::max(min_height_percent, other.min_height_percent), - std::min(max_height_percent, other.max_height_percent), - merge_preferred_size(horizontal_stretch, preferred_width, - other.horizontal_stretch, other.preferred_width), - merge_preferred_size(vertical_stretch, preferred_height, - other.vertical_stretch, other.preferred_height), - std::min(horizontal_stretch, other.horizontal_stretch), - std::min(vertical_stretch, other.vertical_stretch) }; + return LayoutInfo { std::max(min, other.min), + std::min(max, other.max), + std::max(min_percent, other.min_percent), + std::min(max_percent, other.max_percent), + merge_preferred_size(stretch, preferred, + other.stretch, other.preferred), + std::min(stretch, other.stretch) }; } /// FIXME! this should be done by cbindgen namespace cbindgen_private { inline bool operator==(const LayoutInfo &a, const LayoutInfo &b) { - return a.min_width == b.min_width && - a.max_width == b.max_width && - a.min_height == b.min_height && - a.max_height == b.max_height && - a.min_width_percent == b.min_width_percent && - a.max_width_percent == b.max_width_percent && - a.min_height_percent == b.min_height_percent && - a.max_height_percent == b.max_height_percent && - a.preferred_width == b.preferred_width && - a.preferred_height == b.preferred_height && - a.horizontal_stretch == b.horizontal_stretch && - a.vertical_stretch == b.vertical_stretch; + return a.min == b.min && + a.max == b.max && + a.min_percent == b.min_percent && + a.max_percent == b.max_percent && + a.preferred == b.preferred && + a.stretch == b.stretch; } inline bool operator!=(const LayoutInfo &a, const LayoutInfo &b) { diff --git a/sixtyfps_compiler/generator/cpp.rs b/sixtyfps_compiler/generator/cpp.rs index e9de58f06..81649abb3 100644 --- a/sixtyfps_compiler/generator/cpp.rs +++ b/sixtyfps_compiler/generator/cpp.rs @@ -221,7 +221,7 @@ use crate::expression_tree::{ BindingExpression, BuiltinFunction, EasingCurve, Expression, NamedReference, }; use crate::langtype::Type; -use crate::layout::{Layout, LayoutGeometry, LayoutRect}; +use crate::layout::{Layout, LayoutGeometry, LayoutRect, Orientation}; use crate::object_tree::{ Component, Document, Element, ElementRc, PropertyDeclaration, RepeatedElementInfo, }; @@ -272,6 +272,13 @@ fn get_cpp_type(ty: &Type, decl: &PropertyDeclaration, diag: &mut BuildDiagnosti }) } +fn to_cpp_orientation(o: Orientation) -> &'static str { + match o { + Orientation::Horizontal => "sixtyfps::Orientation::Horizontal", + Orientation::Vertical => "sixtyfps::Orientation::Vertical", + } +} + /// If the expression is surrounded with parentheses, remove these parentheses fn remove_parentheses(expr: &str) -> &str { if expr.starts_with('(') && expr.ends_with(')') { @@ -859,8 +866,8 @@ fn generate_component( Access::Public, // Because Repeater accesses it Declaration::Function(Function { name: "box_layout_data".into(), - signature: "() const -> sixtyfps::BoxLayoutCellData".to_owned(), - statements: Some(vec!["return { layout_info({&static_vtable, const_cast(static_cast(this))}) };".into()]), + signature: "(sixtyfps::Orientation o) const -> sixtyfps::BoxLayoutCellData".to_owned(), + statements: Some(vec!["return { layout_info({&static_vtable, const_cast(static_cast(this))}, o) };".into()]), ..Function::default() }), @@ -1194,12 +1201,15 @@ fn generate_component( Declaration::Function(Function { name: "layout_info".into(), signature: - "([[maybe_unused]] sixtyfps::private_api::ComponentRef component) -> sixtyfps::LayoutInfo" + "([[maybe_unused]] sixtyfps::private_api::ComponentRef component, sixtyfps::Orientation o) -> sixtyfps::LayoutInfo" .into(), is_static: true, statements: Some(vec![ format!("[[maybe_unused]] auto self = reinterpret_cast(component.instance);", component_id), - format!("return {};", get_layout_info(&component.root_element, component, &component.root_constraints.borrow())) + format!("if (o == sixtyfps::Orientation::Horizontal) return {};", + get_layout_info(&component.root_element, component, &component.root_constraints.borrow(), Orientation::Horizontal)), + format!("else return {};", + get_layout_info(&component.root_element, component, &component.root_constraints.borrow(), Orientation::Vertical)) ]), ..Default::default() }), @@ -1555,17 +1565,19 @@ fn compile_expression( } } Expression::BuiltinFunctionReference(BuiltinFunction::ImplicitLayoutInfo, _) => { - if arguments.len() != 1 { + if arguments.len() != 2 { panic!("internal error: incorrect argument count to ImplicitLayoutInfo call"); } if let Expression::ElementReference(item) = &arguments[0] { let item = item.upgrade().unwrap(); let item = item.borrow(); let native_item = item.base_type.as_native(); - format!("{vt}->layouting_info({{{vt}, const_cast(&self->{id})}}, &window)", + let orient = compile_expression(&arguments[1], component); + format!("{vt}->layouting_info({{{vt}, const_cast(&self->{id})}}, {o}, &window)", vt = native_item.cpp_vtable_getter, ty = native_item.class_name, - id = item.id + id = item.id, + o = orient, ) } else { panic!("internal error: argument to ImplicitLayoutInfo must be an element") @@ -1697,14 +1709,14 @@ fn compile_expression( Expression::LayoutCacheAccess { layout_cache_prop, index, repeater_index } => { let cache = access_named_reference(layout_cache_prop, component, "self"); if let Some(ri) = repeater_index { - format!("[&](auto cache) {{ return cache[int(cache[{}]) + {} * 4]; }} ({}.get())", index, compile_expression(&ri, component), cache) + format!("[&](auto cache) {{ return cache[int(cache[{}]) + {} * 2]; }} ({}.get())", index, compile_expression(&ri, component), cache) } else { format!("{}.get()[{}]", cache, index) } } - Expression::ComputeLayoutInfo(Layout::GridLayout(layout)) => { - let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, component); - let cells = grid_layout_cell_data(layout, component); + Expression::ComputeLayoutInfo(Layout::GridLayout(layout), o) => { + let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, *o, component); + let cells = grid_layout_cell_data(layout, *o, component); format!("[&] {{ \ const auto padding = {};\ sixtyfps::GridLayoutCellData cells[] = {{ {} }}; \ @@ -1714,58 +1726,63 @@ fn compile_expression( padding, cells, spacing ) } - Expression::ComputeLayoutInfo(Layout::BoxLayout(layout)) => { - let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, component); - let (cells, alignment) = box_layout_data(layout, component, None); + Expression::ComputeLayoutInfo(Layout::BoxLayout(layout), o) => { + let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, *o, component); + let (cells, alignment) = box_layout_data(layout, *o, component, None); + let call = if *o == layout.orientation { + format!("sixtyfps_box_layout_info(slice, {}, &padding, {})", spacing, alignment) + } else { + format!("sixtyfps_box_layout_info_ortho(slice, &padding)") + }; format!("[&] {{ \ const auto padding = {};\ {}\ const sixtyfps::Slice slice{{ &*std::begin(cells), std::size(cells)}}; \ - return sixtyfps::sixtyfps_box_layout_info(slice, {}, &padding, {}, {});\ + return sixtyfps::{};\ }}()", - padding, cells, spacing, alignment, layout.is_horizontal + padding, cells, call ) } - Expression::ComputeLayoutInfo(Layout::PathLayout(_)) => unimplemented!(), - Expression::SolveLayout(Layout::GridLayout(layout)) => { - let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, component); - let cells = grid_layout_cell_data(layout, component); - let (width, height) = layout_geometry_width_height(&layout.geometry.rect, component); + Expression::ComputeLayoutInfo(Layout::PathLayout(_), _) => unimplemented!(), + Expression::SolveLayout(Layout::GridLayout(layout), o) => { + let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, *o, component); + let cells = grid_layout_cell_data(layout, *o, component); + let size = layout_geometry_size(&layout.geometry.rect, *o, component); format!("[&] {{ \ const auto padding = {p};\ sixtyfps::GridLayoutCellData cells[] = {{ {c} }}; \ const sixtyfps::Slice slice{{ cells, std::size(cells)}}; \ - const sixtyfps::GridLayoutData grid {{ {w}, {h}, 0, 0, {s}, &padding, slice }}; + const sixtyfps::GridLayoutData grid {{ {sz}, {s}, &padding, slice }}; sixtyfps::SharedVector result; sixtyfps::sixtyfps_solve_grid_layout(&grid, &result);\ return result; }}()", - p = padding, c = cells, s = spacing, w = width, h = height + p = padding, c = cells, s = spacing, sz = size ) } - Expression::SolveLayout(Layout::BoxLayout(layout)) => { - let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, component); + Expression::SolveLayout(Layout::BoxLayout(layout), o) => { + let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, *o, component); let mut repeated_indices = Default::default(); let mut repeated_indices_init = Default::default(); - let (cells, alignment) = box_layout_data(layout, component, Some((&mut repeated_indices, &mut repeated_indices_init))); - let (width, height) = layout_geometry_width_height(&layout.geometry.rect, component); + let (cells, alignment) = box_layout_data(layout, *o, component, Some((&mut repeated_indices, &mut repeated_indices_init))); + let size = layout_geometry_size(&layout.geometry.rect, *o, component); format!("[&] {{ \ {ri_init}\ const auto padding = {p};\ {c}\ const sixtyfps::Slice slice{{ &*std::begin(cells), std::size(cells)}}; \ - sixtyfps::BoxLayoutData box {{ {w}, {h}, 0, 0, {s}, &padding, {a}, slice }}; + sixtyfps::BoxLayoutData box {{ {sz}, {s}, &padding, {a}, slice }}; sixtyfps::SharedVector result; - sixtyfps::sixtyfps_solve_box_layout(&box, {hz}, {ri}, &result);\ + sixtyfps::sixtyfps_solve_box_layout(&box, {ri}, &result);\ return result; }}()", ri_init = repeated_indices_init, ri = repeated_indices, - p = padding, c = cells, s = spacing, w = width, h = height, a = alignment, - hz = layout.is_horizontal + p = padding, c = cells, s = spacing, sz = size, a = alignment, ) } - Expression::SolveLayout(Layout::PathLayout(layout)) => { - let (width, height) = layout_geometry_width_height(&layout.rect, component); + Expression::SolveLayout(Layout::PathLayout(layout), _) => { + let width = layout_geometry_size(&layout.rect, Orientation::Horizontal, component); + let height = layout_geometry_size(&layout.rect, Orientation::Vertical, component); let elements = compile_path(&layout.path, component); let prop = |expr: &Option| { if let Some(nr) = expr.as_ref() { @@ -1872,18 +1889,21 @@ fn compile_assignment( } } -fn grid_layout_cell_data(layout: &crate::layout::GridLayout, component: &Rc) -> String { +fn grid_layout_cell_data( + layout: &crate::layout::GridLayout, + orientation: Orientation, + component: &Rc, +) -> String { layout .elems .iter() .map(|c| { + let (col_or_row, span) = c.col_or_row_and_span(orientation); format!( - "sixtyfps::GridLayoutCellData {{ {col}, {row}, {cs}, {rs}, {cstr} }}", - col = c.col, - row = c.row, - cs = c.colspan, - rs = c.rowspan, - cstr = get_layout_info(&c.item.element, component, &c.item.constraints), + "sixtyfps::GridLayoutCellData {{ {}, {}, {} }}", + col_or_row, + span, + get_layout_info(&c.item.element, component, &c.item.constraints, orientation), ) }) .join(", ") @@ -1893,6 +1913,7 @@ fn grid_layout_cell_data(layout: &crate::layout::GridLayout, component: &Rc, mut repeated_indices: Option<(&mut String, &mut String)>, ) -> (String, String) { @@ -1909,7 +1930,7 @@ fn box_layout_data( let mut cells = layout.elems.iter().map(|li| { format!( "sixtyfps::BoxLayoutCellData{{ {} }}", - get_layout_info(&li.element, component, &li.constraints) + get_layout_info(&li.element, component, &li.constraints, orientation) ) }); if let Some((ri, _)) = &mut repeated_indices { @@ -1941,13 +1962,14 @@ fn box_layout_data( push_code += &format!( "if (self->repeater_{id}.inner) \ for (auto &&sub_comp : self->repeater_{id}.inner->data) \ - cells.push_back((*sub_comp.ptr)->box_layout_data());", - id = item.element.borrow().id + cells.push_back((*sub_comp.ptr)->box_layout_data({o}));", + id = item.element.borrow().id, + o = to_cpp_orientation(orientation), ); } else { push_code += &format!( "cells.push_back({{ {} }});", - get_layout_info(&item.element, component, &item.constraints) + get_layout_info(&item.element, component, &item.constraints, orientation) ); } } @@ -1957,59 +1979,56 @@ fn box_layout_data( fn generate_layout_padding_and_spacing( layout_geometry: &LayoutGeometry, - + orientation: Orientation, component: &Rc, ) -> (String, String) { - let prop = |expr: &Option| { - if let Some(nr) = expr.as_ref() { + let prop = |expr: Option<&NamedReference>| { + if let Some(nr) = expr { format!("{}.get()", access_named_reference(nr, component, "self")) } else { "0.".into() } }; - let spacing = prop(&layout_geometry.spacing); - let padding = format!( - "sixtyfps::Padding {{ {}, {}, {}, {} }};", - prop(&layout_geometry.padding.left), - prop(&layout_geometry.padding.right), - prop(&layout_geometry.padding.top), - prop(&layout_geometry.padding.bottom), - ); - + let spacing = prop(layout_geometry.spacing.as_ref()); + let (begin, end) = layout_geometry.padding.begin_end(orientation); + let padding = format!("sixtyfps::Padding {{ {}, {} }};", prop(begin), prop(end)); (padding, spacing) } -fn layout_geometry_width_height(rect: &LayoutRect, component: &Rc) -> (String, String) { - let prop = |expr: &Option| { - if let Some(nr) = expr.as_ref() { - format!("{}.get()", access_named_reference(nr, component, "self")) - } else { - "0.".into() - } - }; - (prop(&rect.width_reference), prop(&rect.height_reference)) +fn layout_geometry_size( + rect: &LayoutRect, + orientation: Orientation, + component: &Rc, +) -> String { + rect.size_reference(orientation).map_or_else( + || "0.".into(), + |nr| format!("{}.get()", access_named_reference(nr, component, "self")), + ) } fn get_layout_info( elem: &ElementRc, component: &Rc, constraints: &crate::layout::LayoutConstraints, + orientation: Orientation, ) -> String { - let mut layout_info = if let Some(layout_info_prop) = &elem.borrow().layout_info_prop { + let mut layout_info = if let Some(layout_info_prop) = + &elem.borrow().layout_info_prop(orientation) + { format!("{}.get()", access_named_reference(layout_info_prop, component, "self")) } else { format!( - "{vt}->layouting_info({{{vt}, const_cast(&self->{id})}}, &self->window)", + "{vt}->layouting_info({{{vt}, const_cast(&self->{id})}}, {o}, &self->window)", vt = elem.borrow().base_type.as_native().cpp_vtable_getter, ty = elem.borrow().base_type.as_native().class_name, id = elem.borrow().id, - + o = to_cpp_orientation(orientation), ) }; if constraints.has_explicit_restrictions() { layout_info = format!("[&]{{ auto layout_info = {};", layout_info); - for (expr, name) in constraints.for_each_restrictions() { + for (expr, name) in constraints.for_each_restrictions(orientation) { layout_info += &format!( " layout_info.{} = {}.get();", name,