From 8ae28adb6bb3eacda5ce6b660f26ae54d38f3007 Mon Sep 17 00:00:00 2001 From: David Faure Date: Wed, 15 Oct 2025 15:34:31 +0200 Subject: [PATCH] GridLayout: introduce an Organize step to compute row/col This allows to store the result into a different cache property than the one that comes out of solve_grid_layout, which fixes a binding loop when the text of a widget depends on its row or col value. --- api/cpp/include/slint.h | 25 +- internal/compiler/expression_tree.rs | 29 +++ internal/compiler/langtype.rs | 3 +- internal/compiler/layout.rs | 2 +- internal/compiler/llr/lower_expression.rs | 225 ++++++++++-------- internal/compiler/object_tree.rs | 11 +- internal/compiler/passes/binding_analysis.rs | 29 ++- internal/compiler/passes/const_propagation.rs | 1 + internal/compiler/passes/inlining.rs | 7 + internal/compiler/passes/lower_layout.rs | 117 +++++++-- .../compiler/passes/resolving/remove_noop.rs | 3 + internal/compiler/typeregister.rs | 17 ++ internal/core/layout.rs | 149 +++++++----- internal/interpreter/eval.rs | 17 ++ internal/interpreter/eval_layout.rs | 179 ++++++++------ .../cases/layout/grid_variable_row_col.slint | 17 +- 16 files changed, 557 insertions(+), 274 deletions(-) diff --git a/api/cpp/include/slint.h b/api/cpp/include/slint.h index 142edcf169..d6da6e6254 100644 --- a/api/cpp/include/slint.h +++ b/api/cpp/include/slint.h @@ -123,19 +123,32 @@ inline SharedVector solve_box_layout(const cbindgen_private::BoxLayoutDat return result; } -inline SharedVector solve_grid_layout(const cbindgen_private::GridLayoutData &data, - cbindgen_private::Orientation orientation) +inline SharedVector +organize_grid_layout(cbindgen_private::Slice input_data) { SharedVector result; - cbindgen_private::slint_solve_grid_layout(&data, orientation, &result); + cbindgen_private::slint_organize_grid_layout(input_data, &result); + return result; +} + +inline SharedVector +solve_grid_layout(const cbindgen_private::GridLayoutData &data, + cbindgen_private::Slice constraints, + cbindgen_private::Orientation orientation) +{ + SharedVector result; + cbindgen_private::slint_solve_grid_layout(&data, constraints, orientation, &result); return result; } inline cbindgen_private::LayoutInfo -grid_layout_info(cbindgen_private::Slice cells, float spacing, - const cbindgen_private::Padding &padding, cbindgen_private::Orientation o) +grid_layout_info(const cbindgen_private::GridLayoutOrganizedData &organized_data, + cbindgen_private::Slice constraints, float spacing, + const cbindgen_private::Padding &padding, + cbindgen_private::Orientation orientation) { - return cbindgen_private::slint_grid_layout_info(cells, spacing, &padding, o); + return cbindgen_private::slint_grid_layout_info(&organized_data, constraints, spacing, &padding, + orientation); } inline cbindgen_private::LayoutInfo diff --git a/internal/compiler/expression_tree.rs b/internal/compiler/expression_tree.rs index f5e6e5b8ff..81163f51ac 100644 --- a/internal/compiler/expression_tree.rs +++ b/internal/compiler/expression_tree.rs @@ -752,10 +752,24 @@ pub enum Expression { /// So this looks like `layout_cache_prop[layout_cache_prop[index] + repeater_index]` repeater_index: Option>, }, + + /// Organize a grid layout, i.e. decide what goes where + OrganizeGridLayout(crate::layout::GridLayout), + /// Compute the LayoutInfo for the given layout. /// The orientation is the orientation of the cache, not the orientation of the layout ComputeLayoutInfo(crate::layout::Layout, crate::layout::Orientation), + ComputeGridLayoutInfo { + layout_organized_data_prop: NamedReference, + layout: crate::layout::GridLayout, + orientation: crate::layout::Orientation, + }, SolveLayout(crate::layout::Layout, crate::layout::Orientation), + SolveGridLayout { + layout_organized_data_prop: NamedReference, + layout: crate::layout::GridLayout, + orientation: crate::layout::Orientation, + }, MinMax { ty: Type, @@ -883,8 +897,11 @@ impl Expression { // invalid because the expression is unreachable Expression::ReturnStatement(_) => Type::Invalid, Expression::LayoutCacheAccess { .. } => Type::LogicalLength, + Expression::OrganizeGridLayout(..) => typeregister::organized_layout_type().into(), Expression::ComputeLayoutInfo(..) => typeregister::layout_info_type().into(), + Expression::ComputeGridLayoutInfo { .. } => typeregister::layout_info_type().into(), Expression::SolveLayout(..) => Type::LayoutCache, + Expression::SolveGridLayout { .. } => Type::LayoutCache, Expression::MinMax { ty, .. } => ty.clone(), Expression::EmptyComponentFactory => Type::ComponentFactory, Expression::DebugHook { expression, .. } => expression.ty(), @@ -982,8 +999,11 @@ impl Expression { Expression::LayoutCacheAccess { repeater_index, .. } => { repeater_index.as_deref().map(visitor); } + Expression::OrganizeGridLayout(..) => {} Expression::ComputeLayoutInfo(..) => {} + Expression::ComputeGridLayoutInfo { .. } => {} Expression::SolveLayout(..) => {} + Expression::SolveGridLayout { .. } => {} Expression::MinMax { lhs, rhs, .. } => { visitor(lhs); visitor(rhs); @@ -1086,8 +1106,11 @@ impl Expression { Expression::LayoutCacheAccess { repeater_index, .. } => { repeater_index.as_deref_mut().map(visitor); } + Expression::OrganizeGridLayout(..) => {} Expression::ComputeLayoutInfo(..) => {} + Expression::ComputeGridLayoutInfo { .. } => {} Expression::SolveLayout(..) => {} + Expression::SolveGridLayout { .. } => {} Expression::MinMax { lhs, rhs, .. } => { visitor(lhs); visitor(rhs); @@ -1180,8 +1203,11 @@ impl Expression { } // TODO: detect constant property within layouts Expression::LayoutCacheAccess { .. } => false, + Expression::OrganizeGridLayout { .. } => false, Expression::ComputeLayoutInfo(..) => false, + Expression::ComputeGridLayoutInfo { .. } => false, Expression::SolveLayout(..) => false, + Expression::SolveGridLayout { .. } => false, Expression::MinMax { lhs, rhs, .. } => lhs.is_constant(ga) && rhs.is_constant(ga), Expression::EmptyComponentFactory => true, Expression::DebugHook { .. } => false, @@ -1856,8 +1882,11 @@ pub fn pretty_print(f: &mut dyn std::fmt::Write, expression: &Expression) -> std if repeater_index.is_some() { " + $index" } else { "" } ) } + Expression::OrganizeGridLayout(..) => write!(f, "organize_grid_layout(..)"), Expression::ComputeLayoutInfo(..) => write!(f, "layout_info(..)"), + Expression::ComputeGridLayoutInfo { .. } => write!(f, "grid_layout_info(..)"), Expression::SolveLayout(..) => write!(f, "solve_layout(..)"), + Expression::SolveGridLayout { .. } => write!(f, "solve_grid_layout(..)"), Expression::MinMax { ty: _, op, lhs, rhs } => { match op { MinMaxOp::Min => write!(f, "min(")?, diff --git a/internal/compiler/langtype.rs b/internal/compiler/langtype.rs index a65d655fc5..0c14b68102 100644 --- a/internal/compiler/langtype.rs +++ b/internal/compiler/langtype.rs @@ -644,6 +644,7 @@ pub enum BuiltinPrivateStruct { PropertyAnimation, GridLayoutCellData, GridLayoutData, + GridLayoutInputData, BoxLayoutData, BoxLayoutCellData, Padding, @@ -661,7 +662,7 @@ pub enum BuiltinPrivateStruct { impl BuiltinPrivateStruct { pub fn is_layout_data(&self) -> bool { - matches!(self, Self::GridLayoutData | Self::BoxLayoutData) + matches!(self, Self::GridLayoutInputData | Self::GridLayoutData | Self::BoxLayoutData) } pub fn slint_name(&self) -> Option { match self { diff --git a/internal/compiler/layout.rs b/internal/compiler/layout.rs index 1bf5a46a31..7eb0d8045e 100644 --- a/internal/compiler/layout.rs +++ b/internal/compiler/layout.rs @@ -465,7 +465,7 @@ pub struct GridLayout { } impl GridLayout { - fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) { + pub fn visit_named_references(&mut self, visitor: &mut impl FnMut(&mut NamedReference)) { for cell in &mut self.elems { cell.item.constraints.visit_named_references(visitor); } diff --git a/internal/compiler/llr/lower_expression.rs b/internal/compiler/llr/lower_expression.rs index 18d06c80eb..e1c2376dd6 100644 --- a/internal/compiler/llr/lower_expression.rs +++ b/internal/compiler/llr/lower_expression.rs @@ -246,8 +246,17 @@ pub fn lower_expression( repeater_index: repeater_index.as_ref().map(|e| lower_expression(e, ctx).into()), } } + tree_Expression::OrganizeGridLayout(l) => organize_grid_layout(l, ctx), tree_Expression::ComputeLayoutInfo(l, o) => compute_layout_info(l, *o, ctx), + tree_Expression::ComputeGridLayoutInfo { + layout_organized_data_prop, + layout, + orientation, + } => compute_grid_layout_info(layout_organized_data_prop, layout, *orientation, ctx), tree_Expression::SolveLayout(l, o) => solve_layout(l, *o, ctx), + tree_Expression::SolveGridLayout { layout_organized_data_prop, layout, orientation } => { + solve_grid_layout(layout_organized_data_prop, layout, *orientation, ctx) + } tree_Expression::MinMax { ty, op, lhs, rhs } => llr_Expression::MinMax { ty: ty.clone(), op: *op, @@ -585,24 +594,40 @@ pub fn lower_animation(a: &PropertyAnimation, ctx: &mut ExpressionLoweringCtx<'_ } } +fn compute_grid_layout_info( + layout_organized_data_prop: &NamedReference, + layout: &crate::layout::GridLayout, + o: Orientation, + ctx: &mut ExpressionLoweringCtx, +) -> llr_Expression { + let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx); + let organized_cells = ctx.map_property_reference(layout_organized_data_prop); + let constraints = grid_layout_cell_constraints(layout, o, ctx); + let orientation_literal = llr_Expression::EnumerationValue(EnumerationValue { + value: o as _, + enumeration: crate::typeregister::BUILTIN.with(|b| b.enums.Orientation.clone()), + }); + llr_Expression::ExtraBuiltinFunctionCall { + function: "grid_layout_info".into(), + arguments: vec![ + llr_Expression::PropertyReference(organized_cells), + constraints, + spacing, + padding, + orientation_literal, + ], + return_ty: crate::typeregister::layout_info_type().into(), + } +} + fn compute_layout_info( l: &crate::layout::Layout, o: Orientation, ctx: &mut ExpressionLoweringCtx, ) -> llr_Expression { match l { - crate::layout::Layout::GridLayout(layout) => { - let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx); - let cells = grid_layout_cell_data(layout, o, ctx); - let orientation_literal = llr_Expression::EnumerationValue(EnumerationValue { - value: o as _, - enumeration: crate::typeregister::BUILTIN.with(|b| b.enums.Orientation.clone()), - }); - llr_Expression::ExtraBuiltinFunctionCall { - function: "grid_layout_info".into(), - arguments: vec![cells, spacing, padding, orientation_literal], - return_ty: crate::typeregister::layout_info_type().into(), - } + crate::layout::Layout::GridLayout(_) => { + panic!("compute_layout_info called on GridLayout, use compute_grid_layout_info"); } crate::layout::Layout::BoxLayout(layout) => { let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx); @@ -634,87 +659,56 @@ fn compute_layout_info( } } +fn organize_grid_layout( + layout: &crate::layout::GridLayout, + ctx: &mut ExpressionLoweringCtx, +) -> llr_Expression { + let cells = grid_layout_input_data(layout, ctx); + llr_Expression::ExtraBuiltinFunctionCall { + function: "organize_grid_layout".into(), + arguments: vec![cells], + return_ty: Type::Array(Type::Int32.into()), + } +} + +fn solve_grid_layout( + layout_organized_data_prop: &NamedReference, + layout: &crate::layout::GridLayout, + o: Orientation, + ctx: &mut ExpressionLoweringCtx, +) -> llr_Expression { + let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx); + let cells = ctx.map_property_reference(layout_organized_data_prop); + let size = layout_geometry_size(&layout.geometry.rect, o, ctx); + let orientation_expr = llr_Expression::EnumerationValue(EnumerationValue { + value: o as _, + enumeration: crate::typeregister::BUILTIN.with(|b| b.enums.Orientation.clone()), + }); + llr_Expression::ExtraBuiltinFunctionCall { + function: "solve_grid_layout".into(), + arguments: vec![ + make_struct( + BuiltinPrivateStruct::GridLayoutData, + [ + ("size", Type::Float32, size), + ("spacing", Type::Float32, spacing), + ("padding", padding.ty(ctx), padding), + ("organized_data", Type::LayoutCache, llr_Expression::PropertyReference(cells)), + ], + ), + grid_layout_cell_constraints(layout, o, ctx), + orientation_expr, + ], + return_ty: Type::LayoutCache, + } +} + fn solve_layout( l: &crate::layout::Layout, o: Orientation, ctx: &mut ExpressionLoweringCtx, ) -> llr_Expression { match l { - crate::layout::Layout::GridLayout(layout) => { - let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx); - let cells = grid_layout_cell_data(layout, o, ctx); - let size = layout_geometry_size(&layout.geometry.rect, o, ctx); - let orientation_expr = llr_Expression::EnumerationValue(EnumerationValue { - value: o as _, - enumeration: crate::typeregister::BUILTIN.with(|b| b.enums.Orientation.clone()), - }); - if let (Some(button_roles), Orientation::Horizontal) = (&layout.dialog_button_roles, o) - { - let cells_ty = cells.ty(ctx); - let e = crate::typeregister::BUILTIN.with(|e| e.enums.DialogButtonRole.clone()); - let roles = button_roles - .iter() - .map(|r| { - llr_Expression::EnumerationValue(EnumerationValue { - value: e.values.iter().position(|x| x == r).unwrap() as _, - enumeration: e.clone(), - }) - }) - .collect(); - llr_Expression::CodeBlock(vec![ - llr_Expression::ComputeDialogLayoutCells { - cells_variable: "cells".into(), - roles: llr_Expression::Array { - element_ty: Type::Enumeration(e), - values: roles, - as_model: false, - } - .into(), - unsorted_cells: Box::new(cells), - }, - llr_Expression::ExtraBuiltinFunctionCall { - function: "solve_grid_layout".into(), - arguments: vec![ - make_struct( - BuiltinPrivateStruct::GridLayoutData, - [ - ("size", Type::Float32, size), - ("spacing", Type::Float32, spacing), - ("padding", padding.ty(ctx), padding), - ( - "cells", - cells_ty.clone(), - llr_Expression::ReadLocalVariable { - name: "cells".into(), - ty: cells_ty, - }, - ), - ], - ), - orientation_expr, - ], - return_ty: Type::LayoutCache, - }, - ]) - } else { - llr_Expression::ExtraBuiltinFunctionCall { - function: "solve_grid_layout".into(), - arguments: vec![ - make_struct( - BuiltinPrivateStruct::GridLayoutData, - [ - ("size", Type::Float32, size), - ("spacing", Type::Float32, spacing), - ("padding", padding.ty(ctx), padding), - ("cells", cells.ty(ctx), cells), - ], - ), - orientation_expr, - ], - return_ty: Type::LayoutCache, - } - } - } crate::layout::Layout::BoxLayout(layout) => { let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx); let bld = box_layout_data(layout, o, ctx); @@ -766,6 +760,7 @@ fn solve_layout( }, } } + _ => panic!("solve_layout is only supported for BoxLayout"), } } @@ -846,20 +841,32 @@ fn box_layout_data( } } -fn grid_layout_cell_data( +fn grid_layout_cell_constraints( layout: &crate::layout::GridLayout, orientation: Orientation, ctx: &mut ExpressionLoweringCtx, ) -> llr_Expression { llr_Expression::Array { - element_ty: grid_layout_cell_data_ty(), + element_ty: crate::typeregister::layout_info_type().into(), + values: layout + .elems + .iter() + .map(|c| get_layout_info(&c.item.element, ctx, &c.item.constraints, orientation)) + .collect(), + as_model: false, + } +} + +fn grid_layout_input_data( + layout: &crate::layout::GridLayout, + ctx: &mut ExpressionLoweringCtx, +) -> llr_Expression { + llr_Expression::Array { + element_ty: grid_layout_input_data_ty(), values: layout .elems .iter() .map(|c| { - let layout_info = - get_layout_info(&c.item.element, ctx, &c.item.constraints, orientation); - let mut lower_expr_or_default = |expr: &Option, default: u16| { expr.as_ref().map_or_else( @@ -870,23 +877,17 @@ fn grid_layout_cell_data( // MAX means "auto", see to_layout_data() let row_expr = lower_expr_or_default(&c.row_expr, u16::MAX); let col_expr = lower_expr_or_default(&c.col_expr, u16::MAX); - let span_expr = lower_expr_or_default(&c.span(orientation), 1); + let rowspan_expr = lower_expr_or_default(&c.rowspan_expr, 1); + let colspan_expr = lower_expr_or_default(&c.colspan_expr, 1); make_struct( - BuiltinPrivateStruct::GridLayoutCellData, + BuiltinPrivateStruct::GridLayoutInputData, [ - ("constraint", crate::typeregister::layout_info_type().into(), layout_info), ("new_row", Type::Bool, llr_Expression::BoolLiteral(c.new_row)), - ( - "col_or_row", - Type::Int32, - match orientation { - Orientation::Horizontal => col_expr, - Orientation::Vertical => row_expr.clone(), - }, - ), ("row", Type::Int32, row_expr), - ("span", Type::Int32, span_expr), + ("col", Type::Int32, col_expr), + ("rowspan", Type::Int32, rowspan_expr), + ("colspan", Type::Int32, colspan_expr), ], ) }) @@ -898,7 +899,6 @@ fn grid_layout_cell_data( pub(super) fn grid_layout_cell_data_ty() -> Type { Type::Struct(Rc::new(Struct { fields: IntoIterator::into_iter([ - (SmolStr::new_static("new_row"), Type::Bool), (SmolStr::new_static("col_or_row"), Type::Int32), (SmolStr::new_static("span"), Type::Int32), (SmolStr::new_static("constraint"), crate::typeregister::layout_info_type().into()), @@ -908,6 +908,21 @@ pub(super) fn grid_layout_cell_data_ty() -> Type { })) } +pub(super) fn grid_layout_input_data_ty() -> Type { + Type::Struct(Rc::new(Struct { + fields: IntoIterator::into_iter([ + (SmolStr::new_static("new_row"), Type::Bool), + (SmolStr::new_static("row"), Type::Int32), + (SmolStr::new_static("col"), Type::Int32), + (SmolStr::new_static("rowspan"), Type::Int32), + (SmolStr::new_static("colspan"), Type::Int32), + ]) + .collect(), + name: BuiltinPrivateStruct::GridLayoutInputData.into(), + rust_attributes: None, + })) +} + fn generate_layout_padding_and_spacing( layout_geometry: &crate::layout::LayoutGeometry, orientation: Orientation, diff --git a/internal/compiler/object_tree.rs b/internal/compiler/object_tree.rs index b1df8c4ee0..3722691646 100644 --- a/internal/compiler/object_tree.rs +++ b/internal/compiler/object_tree.rs @@ -2374,8 +2374,17 @@ pub fn visit_named_references_in_expression( .. } => vis(r), Expression::LayoutCacheAccess { layout_cache_prop, .. } => vis(layout_cache_prop), - Expression::SolveLayout(l, _) => l.visit_named_references(vis), + Expression::OrganizeGridLayout(l) => l.visit_named_references(vis), Expression::ComputeLayoutInfo(l, _) => l.visit_named_references(vis), + Expression::ComputeGridLayoutInfo { layout_organized_data_prop, layout, .. } => { + vis(layout_organized_data_prop); + layout.visit_named_references(vis); + } + Expression::SolveLayout(l, _) => l.visit_named_references(vis), + Expression::SolveGridLayout { layout_organized_data_prop, layout, .. } => { + vis(layout_organized_data_prop); + layout.visit_named_references(vis); + } // This is not really a named reference, but the result is the same, it need to be updated // FIXME: this should probably be lowered into a PropertyReference Expression::RepeaterModelReference { element } diff --git a/internal/compiler/passes/binding_analysis.rs b/internal/compiler/passes/binding_analysis.rs index 95e18ea0f3..45214afbcd 100644 --- a/internal/compiler/passes/binding_analysis.rs +++ b/internal/compiler/passes/binding_analysis.rs @@ -498,13 +498,13 @@ fn recurse_expression( Expression::SolveLayout(l, o) | Expression::ComputeLayoutInfo(l, o) => { // we should only visit the layout geometry for the orientation if matches!(expr, Expression::SolveLayout(..)) { - if let Some(nr) = l.rect().size_reference(*o) { + if let Some(nr) = l.geometry().rect.size_reference(*o) { vis(&nr.clone().into(), P); } } match l { - crate::layout::Layout::GridLayout(l) => { - visit_layout_items_dependencies(l.elems.iter().map(|it| &it.item), *o, vis) + crate::layout::Layout::GridLayout(_) => { + panic!("GridLayout should use SolveGridLayout/ComputeGridLayoutInfo") } crate::layout::Layout::BoxLayout(l) => { visit_layout_items_dependencies(l.elems.iter(), *o, vis) @@ -515,6 +515,29 @@ fn recurse_expression( g.rect = Default::default(); // already visited; g.visit_named_references(&mut |nr| vis(&nr.clone().into(), P)) } + Expression::OrganizeGridLayout(layout) => { + let mut g = layout.geometry.clone(); + g.rect = Default::default(); + g.visit_named_references(&mut |nr| vis(&nr.clone().into(), P)) + } + Expression::SolveGridLayout { layout_organized_data_prop, layout, orientation } + | Expression::ComputeGridLayoutInfo { layout_organized_data_prop, layout, orientation } => { + // we should only visit the layout geometry for the orientation + if matches!(expr, Expression::SolveGridLayout { .. }) { + if let Some(nr) = layout.geometry.rect.size_reference(*orientation) { + vis(&nr.clone().into(), P); + } + } + vis(&layout_organized_data_prop.clone().into(), P); + visit_layout_items_dependencies( + layout.elems.iter().map(|it| &it.item), + *orientation, + vis, + ); + let mut g = layout.geometry.clone(); + g.rect = Default::default(); // already visited; + g.visit_named_references(&mut |nr| vis(&nr.clone().into(), P)) + } Expression::FunctionCall { function: Callable::Callback(nr) | Callable::Function(nr), .. diff --git a/internal/compiler/passes/const_propagation.rs b/internal/compiler/passes/const_propagation.rs index 1b4b39f390..ebe9ef7249 100644 --- a/internal/compiler/passes/const_propagation.rs +++ b/internal/compiler/passes/const_propagation.rs @@ -211,6 +211,7 @@ fn simplify_expression(expr: &mut Expression, ga: &GlobalAnalysis) -> bool { } Expression::ElementReference { .. } => false, Expression::LayoutCacheAccess { .. } => false, + Expression::OrganizeGridLayout { .. } => false, Expression::SolveLayout { .. } => false, Expression::ComputeLayoutInfo { .. } => false, _ => { diff --git a/internal/compiler/passes/inlining.rs b/internal/compiler/passes/inlining.rs index 7c6dd9bbcf..fe799469ec 100644 --- a/internal/compiler/passes/inlining.rs +++ b/internal/compiler/passes/inlining.rs @@ -593,6 +593,13 @@ fn fixup_element_references(expr: &mut Expression, mapping: &Mapping) { } } }, + Expression::SolveGridLayout { layout, .. } + | Expression::OrganizeGridLayout(layout) + | Expression::ComputeGridLayoutInfo { layout, .. } => { + for e in &mut layout.elems { + fxe(&mut e.item.element); + } + } Expression::RepeaterModelReference { element } | Expression::RepeaterIndexReference { element } => fx(element), _ => expr.visit_mut(|e| fixup_element_references(e, mapping)), diff --git a/internal/compiler/passes/lower_layout.rs b/internal/compiler/passes/lower_layout.rs index 42fe82c006..afff03cce3 100644 --- a/internal/compiler/passes/lower_layout.rs +++ b/internal/compiler/passes/lower_layout.rs @@ -12,7 +12,7 @@ use crate::langtype::Type; use crate::layout::*; use crate::object_tree::*; use crate::typeloader::TypeLoader; -use crate::typeregister::{TypeRegister, layout_info_type}; +use crate::typeregister::{TypeRegister, layout_info_type, organized_layout_type}; use smol_str::{SmolStr, format_smolstr}; use std::cell::RefCell; use std::collections::HashSet; @@ -163,6 +163,11 @@ fn lower_grid_layout( dialog_button_roles: None, }; + let layout_organized_data_prop = create_new_prop( + grid_layout_element, + SmolStr::new_static("layout-organized-data"), + organized_layout_type().into(), + ); let layout_cache_prop_h = create_new_prop( grid_layout_element, SmolStr::new_static("layout-cache-h"), @@ -203,7 +208,14 @@ fn lower_grid_layout( &*binding.borrow(), ); }); - grid.add_element(&x, new_row, &layout_cache_prop_h, &layout_cache_prop_v, diag); + grid.add_element( + &x, + new_row, + &layout_cache_prop_h, + &layout_cache_prop_v, + &layout_organized_data_prop, + diag, + ); collected_children.push(x); new_row = false; } @@ -221,6 +233,7 @@ fn lower_grid_layout( new_row, &layout_cache_prop_h, &layout_cache_prop_v, + &layout_organized_data_prop, diag, ); collected_children.push(layout_child); @@ -229,10 +242,23 @@ fn lower_grid_layout( } grid_layout_element.borrow_mut().children = collected_children; let span = grid_layout_element.borrow().to_source_location(); + + layout_organized_data_prop.element().borrow_mut().bindings.insert( + layout_organized_data_prop.name().clone(), + BindingExpression::new_with_span( + Expression::OrganizeGridLayout(grid.clone()), + span.clone(), + ) + .into(), + ); layout_cache_prop_h.element().borrow_mut().bindings.insert( layout_cache_prop_h.name().clone(), BindingExpression::new_with_span( - Expression::SolveLayout(Layout::GridLayout(grid.clone()), Orientation::Horizontal), + Expression::SolveGridLayout { + layout_organized_data_prop: layout_organized_data_prop.clone(), + layout: grid.clone(), + orientation: Orientation::Horizontal, + }, span.clone(), ) .into(), @@ -240,7 +266,11 @@ fn lower_grid_layout( layout_cache_prop_v.element().borrow_mut().bindings.insert( layout_cache_prop_v.name().clone(), BindingExpression::new_with_span( - Expression::SolveLayout(Layout::GridLayout(grid.clone()), Orientation::Vertical), + Expression::SolveGridLayout { + layout_organized_data_prop: layout_organized_data_prop.clone(), + layout: grid.clone(), + orientation: Orientation::Vertical, + }, span.clone(), ) .into(), @@ -248,10 +278,11 @@ fn lower_grid_layout( layout_info_prop_h.element().borrow_mut().bindings.insert( layout_info_prop_h.name().clone(), BindingExpression::new_with_span( - Expression::ComputeLayoutInfo( - Layout::GridLayout(grid.clone()), - Orientation::Horizontal, - ), + Expression::ComputeGridLayoutInfo { + layout_organized_data_prop: layout_organized_data_prop.clone(), + layout: grid.clone(), + orientation: Orientation::Horizontal, + }, span.clone(), ) .into(), @@ -259,7 +290,11 @@ fn lower_grid_layout( layout_info_prop_v.element().borrow_mut().bindings.insert( layout_info_prop_v.name().clone(), BindingExpression::new_with_span( - Expression::ComputeLayoutInfo(Layout::GridLayout(grid.clone()), Orientation::Vertical), + Expression::ComputeGridLayoutInfo { + layout_organized_data_prop: layout_organized_data_prop.clone(), + layout: grid.clone(), + orientation: Orientation::Vertical, + }, span, ) .into(), @@ -278,6 +313,7 @@ impl GridLayout { new_row: bool, layout_cache_prop_h: &NamedReference, layout_cache_prop_v: &NamedReference, + organized_data_prop: &NamedReference, diag: &mut BuildDiagnostics, ) { let mut get_expr = |name: &str| { @@ -300,6 +336,7 @@ impl GridLayout { (&rowspan_expr, &colspan_expr), layout_cache_prop_h, layout_cache_prop_v, + organized_data_prop, diag, ); } @@ -311,6 +348,7 @@ impl GridLayout { (rowspan, colspan): (u16, u16), layout_cache_prop_h: &NamedReference, layout_cache_prop_v: &NamedReference, + organized_data_prop: &NamedReference, diag: &mut BuildDiagnostics, ) { self.add_element_with_coord_as_expr( @@ -326,6 +364,7 @@ impl GridLayout { ), layout_cache_prop_h, layout_cache_prop_v, + organized_data_prop, diag, ) } @@ -338,6 +377,7 @@ impl GridLayout { (rowspan_expr, colspan_expr): (&Option, &Option), layout_cache_prop_h: &NamedReference, layout_cache_prop_v: &NamedReference, + organized_data_prop: &NamedReference, diag: &mut BuildDiagnostics, ) { let index = self.elems.len(); @@ -353,20 +393,21 @@ impl GridLayout { } let e = &layout_item.elem; - set_prop_from_cache(e, "x", layout_cache_prop_h, index * 3, &None, diag); + set_prop_from_cache(e, "x", layout_cache_prop_h, index * 2, &None, diag); if !layout_item.item.constraints.fixed_width { - set_prop_from_cache(e, "width", layout_cache_prop_h, index * 3 + 1, &None, diag); + set_prop_from_cache(e, "width", layout_cache_prop_h, index * 2 + 1, &None, diag); } - if col_expr.is_none() { - set_prop_from_cache(e, "col", layout_cache_prop_h, index * 3 + 2, &None, diag); + set_prop_from_cache(e, "y", layout_cache_prop_v, index * 2, &None, diag); + if !layout_item.item.constraints.fixed_height { + set_prop_from_cache(e, "height", layout_cache_prop_v, index * 2 + 1, &None, diag); } - set_prop_from_cache(e, "y", layout_cache_prop_v, index * 3, &None, diag); - if !layout_item.item.constraints.fixed_height { - set_prop_from_cache(e, "height", layout_cache_prop_v, index * 3 + 1, &None, diag); + let org_index = index * 4; + if col_expr.is_none() { + set_prop_from_cache(e, "col", organized_data_prop, org_index, &None, diag); } if row_expr.is_none() { - set_prop_from_cache(e, "row", layout_cache_prop_v, index * 3 + 2, &None, diag); + set_prop_from_cache(e, "row", organized_data_prop, org_index + 2, &None, diag); } self.elems.push(GridLayoutElement { @@ -542,6 +583,11 @@ fn lower_dialog_layout( .vertical .get_or_insert(NamedReference::new(metrics, SmolStr::new_static("layout-spacing"))); + let layout_organized_data_prop = create_new_prop( + dialog_element, + SmolStr::new_static("layout-organized-data"), + organized_layout_type().into(), + ); let layout_cache_prop_h = create_new_prop(dialog_element, SmolStr::new_static("layout-cache-h"), Type::LayoutCache); let layout_cache_prop_v = @@ -672,6 +718,7 @@ fn lower_dialog_layout( (1, 1), &layout_cache_prop_h, &layout_cache_prop_v, + &layout_organized_data_prop, diag, ); } else if main_widget.is_some() { @@ -692,6 +739,7 @@ fn lower_dialog_layout( (1, button_roles.len() as u16 + 1), &layout_cache_prop_h, &layout_cache_prop_v, + &layout_organized_data_prop, diag, ); } else { @@ -703,10 +751,22 @@ fn lower_dialog_layout( grid.dialog_button_roles = Some(button_roles); let span = dialog_element.borrow().to_source_location(); + layout_organized_data_prop.element().borrow_mut().bindings.insert( + layout_organized_data_prop.name().clone(), + BindingExpression::new_with_span( + Expression::OrganizeGridLayout(grid.clone()), + span.clone(), + ) + .into(), + ); layout_cache_prop_h.element().borrow_mut().bindings.insert( layout_cache_prop_h.name().clone(), BindingExpression::new_with_span( - Expression::SolveLayout(Layout::GridLayout(grid.clone()), Orientation::Horizontal), + Expression::SolveGridLayout { + layout_organized_data_prop: layout_organized_data_prop.clone(), + layout: grid.clone(), + orientation: Orientation::Horizontal, + }, span.clone(), ) .into(), @@ -714,7 +774,11 @@ fn lower_dialog_layout( layout_cache_prop_v.element().borrow_mut().bindings.insert( layout_cache_prop_v.name().clone(), BindingExpression::new_with_span( - Expression::SolveLayout(Layout::GridLayout(grid.clone()), Orientation::Vertical), + Expression::SolveGridLayout { + layout_organized_data_prop: layout_organized_data_prop.clone(), + layout: grid.clone(), + orientation: Orientation::Vertical, + }, span.clone(), ) .into(), @@ -722,10 +786,11 @@ fn lower_dialog_layout( layout_info_prop_h.element().borrow_mut().bindings.insert( layout_info_prop_h.name().clone(), BindingExpression::new_with_span( - Expression::ComputeLayoutInfo( - Layout::GridLayout(grid.clone()), - Orientation::Horizontal, - ), + Expression::ComputeGridLayoutInfo { + layout_organized_data_prop: layout_organized_data_prop.clone(), + layout: grid.clone(), + orientation: Orientation::Horizontal, + }, span.clone(), ) .into(), @@ -733,7 +798,11 @@ fn lower_dialog_layout( layout_info_prop_v.element().borrow_mut().bindings.insert( layout_info_prop_v.name().clone(), BindingExpression::new_with_span( - Expression::ComputeLayoutInfo(Layout::GridLayout(grid.clone()), Orientation::Vertical), + Expression::ComputeGridLayoutInfo { + layout_organized_data_prop: layout_organized_data_prop.clone(), + layout: grid.clone(), + orientation: Orientation::Vertical, + }, span, ) .into(), diff --git a/internal/compiler/passes/resolving/remove_noop.rs b/internal/compiler/passes/resolving/remove_noop.rs index 9ae85fe4c4..c91da44059 100644 --- a/internal/compiler/passes/resolving/remove_noop.rs +++ b/internal/compiler/passes/resolving/remove_noop.rs @@ -103,7 +103,10 @@ fn without_side_effects(expression: &Expression) -> bool { Expression::ReturnStatement(_) => false, Expression::LayoutCacheAccess { .. } => false, Expression::ComputeLayoutInfo(_, _) => false, + Expression::ComputeGridLayoutInfo { .. } => false, + Expression::OrganizeGridLayout(_) => false, Expression::SolveLayout(_, _) => false, + Expression::SolveGridLayout { .. } => false, Expression::MinMax { ty: _, op: _, lhs, rhs } => { without_side_effects(lhs) && without_side_effects(rhs) } diff --git a/internal/compiler/typeregister.rs b/internal/compiler/typeregister.rs index a7584a5904..b7751590d4 100644 --- a/internal/compiler/typeregister.rs +++ b/internal/compiler/typeregister.rs @@ -85,6 +85,7 @@ pub struct BuiltinTypes { pub logical_point_type: Rc, pub font_metrics_type: Type, pub layout_info_type: Rc, + pub gridlayout_input_data_type: Type, pub path_element_type: Type, pub box_layout_cell_data_type: Type, } @@ -143,6 +144,17 @@ impl BuiltinTypes { .collect(), name: BuiltinPrivateStruct::BoxLayoutCellData.into(), })), + gridlayout_input_data_type: Type::Struct(Rc::new(Struct { + fields: IntoIterator::into_iter([ + ("row".into(), Type::Int32), + ("column".into(), Type::Int32), + ("rowspan".into(), Type::Int32), + ("colspan".into(), Type::Int32), + ]) + .collect(), + name: BuiltinPrivateStruct::GridLayoutInputData.into(), + rust_attributes: None, + })), } } } @@ -720,6 +732,11 @@ pub fn layout_info_type() -> Rc { BUILTIN.with(|types| types.layout_info_type.clone()) } +/// The [`Type`] for a runtime GridLayoutOrganizedData structure +pub fn organized_layout_type() -> Type { + Type::LayoutCache +} + /// The [`Type`] for a runtime PathElement structure pub fn path_element_type() -> Type { BUILTIN.with(|types| types.path_element_type.clone()) diff --git a/internal/core/layout.rs b/internal/core/layout.rs index 8944a96e6d..88d00c51f4 100644 --- a/internal/core/layout.rs +++ b/internal/core/layout.rs @@ -305,23 +305,39 @@ mod grid_internal { assert_eq!(my_items[2].size, 100.); } + pub fn to_layout_cell_data( + organized_data: &GridLayoutOrganizedData, + constraints: Slice, + orientation: Orientation, + ) -> Vec { + assert!(organized_data.len() % 4 == 0); + assert!(constraints.len() * 4 == organized_data.len()); + + let mut cells: Vec = Vec::with_capacity(organized_data.len() / 4); + let offset = if orientation == Orientation::Horizontal { 0 } else { 2 }; + for (idx, constraint) in constraints.iter().enumerate() { + cells.push(GridLayoutCellData { + col_or_row: organized_data[idx * 4 + offset] as u16, + span: organized_data[idx * 4 + offset + 1] as u16, + constraint: *constraint, + }); + } + cells + } + /// Create a vector of LayoutData (e.g. one per row if Vertical) for an array of GridLayoutCellData (one per cell) - /// Used by both solve_grid_layout() and grid_layout_info() (each with their own copy of the GridLayoutCellData slice) + /// Used by both solve_grid_layout() and grid_layout_info() pub fn to_layout_data( - data: &[GridLayoutCellData], + cells: &[GridLayoutCellData], spacing: Coord, size: Option, - orientation: Orientation, - ) -> (Vec, Vec) { - let mut cells: Vec = data.to_vec(); - determine_row_col_numbers(&mut cells, orientation); - + ) -> Vec { let mut num = 0usize; for cell in cells.iter() { num = num.max(cell.col_or_row as usize + cell.span.max(1) as usize); } if num < 1 { - return (Default::default(), cells); + return Default::default(); } let mut layout_data = alloc::vec![grid_internal::LayoutData { stretch: 1., ..Default::default() }; num]; @@ -402,7 +418,7 @@ mod grid_internal { } } } - (layout_data, cells) + layout_data } } @@ -427,74 +443,91 @@ pub struct Padding { #[repr(C)] #[derive(Debug)] -pub struct GridLayoutData<'a> { +/// The horizontal or vertical data for all cells of a GridLayout, used as input to solve_grid_layout() +pub struct GridLayoutData { pub size: Coord, pub spacing: Coord, pub padding: Padding, - pub cells: Slice<'a, GridLayoutCellData>, + pub organized_data: GridLayoutOrganizedData, } /// The horizontal or vertical data for a cell of a GridLayout #[repr(C)] -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, PartialEq)] pub struct GridLayoutCellData { - /// whether this cell is the first one in a Row element - pub new_row: bool, /// col or row (u16::MAX means auto). pub col_or_row: u16, - /// row number, needed to detect row changes (and set col to 0). - /// This duplicates col_or_row in case of vertical data, but col_or_row allows more common code. - pub row: u16, /// colspan or rowspan pub span: u16, pub constraint: LayoutInfo, } +/// The input data for a cell of a GridLayout, before row/col determination and before H/V split +/// Used as input to organize_grid_layout() +#[repr(C)] +#[derive(Default, Debug, Clone)] +pub struct GridLayoutInputData { + /// whether this cell is the first one in a Row element + pub new_row: bool, + /// col and row number (u16::MAX means auto). + pub col: u16, + pub row: u16, + /// colspan and rowspan + pub colspan: u16, + pub rowspan: u16, +} + +/// The organized layout data for a GridLayout, after row/col determination: +/// For each cell, stores col, colspan, row, rowspan +pub type GridLayoutOrganizedData = SharedVector; + +impl GridLayoutOrganizedData { + pub fn push_cell(&mut self, col: u16, colspan: u16, row: u16, rowspan: u16) { + self.push(col as Coord); + self.push(colspan as Coord); + self.push(row as Coord); + self.push(rowspan as Coord); + } +} + // Implement "auto" behavior for row/col numbers (unless specified in the slint file). -// Shared code between to_layout_data() (when compiled) and eval_layout.rs (when interpreted) -pub fn determine_row_col_numbers(data: &mut [GridLayoutCellData], orientation: Orientation) { +pub fn organize_grid_layout(core_slice: Slice) -> GridLayoutOrganizedData { + let data = core_slice.as_slice(); + let mut organized_data = GridLayoutOrganizedData::default(); + organized_data.reserve(data.len() * 4); let mut row = 0; let mut col = 0; let mut first = true; let auto = u16::MAX; - for cell in data.iter_mut() { + for cell in data.iter() { if cell.new_row && !first { row += 1; col = 0; } first = false; - if cell.row == auto { - cell.row = row; - } else if row != cell.row { + if cell.row != auto && row != cell.row { row = cell.row; col = 0; } - if orientation == Orientation::Horizontal { - if cell.col_or_row == auto { - cell.col_or_row = col; - } else { - col = cell.col_or_row; - } - } else { - if cell.col_or_row == auto { - cell.col_or_row = row; - } else { - row = cell.col_or_row; - } + if cell.col != auto { + col = cell.col; } + + organized_data.push_cell(col, cell.colspan, row, cell.rowspan); col += 1; } + organized_data } -/// return, an array which is of size `data.cells.len() * 3` which for each cell stores: -/// pos (x or y), size (width or height), row or column number -pub fn solve_grid_layout(data: &GridLayoutData, orientation: Orientation) -> SharedVector { - let (mut layout_data, cells) = grid_internal::to_layout_data( - data.cells.as_slice(), - data.spacing, - Some(data.size), - orientation, - ); +/// return, an array which is of size `data.cells.len() * 2` which for each cell stores: +/// pos (x or y), size (width or height) +pub fn solve_grid_layout( + data: &GridLayoutData, + constraints: Slice, + orientation: Orientation, +) -> SharedVector { + let cells = grid_internal::to_layout_cell_data(&data.organized_data, constraints, orientation); + let mut layout_data = grid_internal::to_layout_data(&cells, data.spacing, Some(data.size)); if layout_data.is_empty() { return Default::default(); @@ -507,8 +540,8 @@ pub fn solve_grid_layout(data: &GridLayoutData, orientation: Orientation) -> Sha data.spacing, ); - let mut result = SharedVector::with_capacity(4 * cells.len()); - for cell in cells.iter() { + let mut result = SharedVector::with_capacity(2 * cells.len()); + for cell in cells.as_slice().iter() { let cdata = &layout_data[cell.col_or_row as usize]; result.push(cdata.pos); result.push(if cell.span > 0 { @@ -518,19 +551,19 @@ pub fn solve_grid_layout(data: &GridLayoutData, orientation: Orientation) -> Sha } else { 0 as Coord }); - result.push(cell.col_or_row as _); } result } pub fn grid_layout_info( - cells: Slice, + organized_data: GridLayoutOrganizedData, // not & because the code generator doesn't support it in ExtraBuiltinFunctionCall + constraints: Slice, spacing: Coord, padding: &Padding, orientation: Orientation, ) -> LayoutInfo { - let (layout_data, _) = - grid_internal::to_layout_data(cells.as_slice(), spacing, None, orientation); + let cells = grid_internal::to_layout_cell_data(&organized_data, constraints, orientation); + let layout_data = grid_internal::to_layout_data(&cells, spacing, None); if layout_data.is_empty() { return Default::default(); } @@ -799,23 +832,33 @@ pub(crate) mod ffi { use super::*; + #[unsafe(no_mangle)] + pub extern "C" fn slint_organize_grid_layout( + input_data: Slice, + result: &mut GridLayoutOrganizedData, + ) { + *result = super::organize_grid_layout(input_data); + } + #[unsafe(no_mangle)] pub extern "C" fn slint_solve_grid_layout( data: &GridLayoutData, + constraints: Slice, orientation: Orientation, result: &mut SharedVector, ) { - *result = super::solve_grid_layout(data, orientation) + *result = super::solve_grid_layout(data, constraints, orientation) } #[unsafe(no_mangle)] pub extern "C" fn slint_grid_layout_info( - cells: Slice, + organized_data: &GridLayoutOrganizedData, + constraints: Slice, spacing: Coord, padding: &Padding, orientation: Orientation, ) -> LayoutInfo { - super::grid_layout_info(cells, spacing, padding, orientation) + super::grid_layout_info(organized_data.clone(), constraints, spacing, padding, orientation) } #[unsafe(no_mangle)] diff --git a/internal/interpreter/eval.rs b/internal/interpreter/eval.rs index f60ab172d3..1c69ffb1a8 100644 --- a/internal/interpreter/eval.rs +++ b/internal/interpreter/eval.rs @@ -428,7 +428,24 @@ pub fn eval_expression(expression: &Expression, local_context: &mut EvalLocalCon } } Expression::ComputeLayoutInfo(lay, o) => crate::eval_layout::compute_layout_info(lay, *o, local_context), + Expression::ComputeGridLayoutInfo { layout_organized_data_prop, layout, orientation } => { + let cache = load_property_helper(&ComponentInstance::InstanceRef(local_context.component_instance), &layout_organized_data_prop.element(), layout_organized_data_prop.name()).unwrap(); + if let Value::LayoutCache(cache) = cache { + crate::eval_layout::compute_grid_layout_info(layout, &cache, *orientation, local_context) + } else { + panic!("invalid layout organized data cache") + } + } + Expression::OrganizeGridLayout(lay) => crate::eval_layout::organize_grid_layout(lay, local_context), Expression::SolveLayout(lay, o) => crate::eval_layout::solve_layout(lay, *o, local_context), + Expression::SolveGridLayout { layout_organized_data_prop, layout, orientation } => { + let cache = load_property_helper(&ComponentInstance::InstanceRef(local_context.component_instance), &layout_organized_data_prop.element(), layout_organized_data_prop.name()).unwrap(); + if let Value::LayoutCache(cache) = cache { + crate::eval_layout::solve_grid_layout(&cache, layout, *orientation, local_context) + } else { + panic!("invalid layout organized data cache") + } + } Expression::MinMax { ty: _, op, lhs, rhs } => { let Value::Number(lhs) = eval_expression(lhs, local_context) else { return local_context.return_value.clone().expect("minmax lhs expression did not evaluate to number"); diff --git a/internal/interpreter/eval_layout.rs b/internal/interpreter/eval_layout.rs index e4c3401f49..c111d5d663 100644 --- a/internal/interpreter/eval_layout.rs +++ b/internal/interpreter/eval_layout.rs @@ -6,11 +6,13 @@ use crate::eval::{self, eval_expression, EvalLocalContext}; use crate::Value; use i_slint_compiler::expression_tree::Expression; use i_slint_compiler::langtype::Type; -use i_slint_compiler::layout::{Layout, LayoutConstraints, LayoutGeometry, Orientation}; +use i_slint_compiler::layout::{ + GridLayout, Layout, LayoutConstraints, LayoutGeometry, Orientation, +}; use i_slint_compiler::namedreference::NamedReference; use i_slint_compiler::object_tree::ElementRc; use i_slint_core::items::{DialogButtonRole, ItemRc}; -use i_slint_core::layout::{self as core_layout}; +use i_slint_core::layout::{self as core_layout, GridLayoutOrganizedData}; use i_slint_core::model::RepeatedItemTree; use i_slint_core::slice::Slice; use i_slint_core::window::WindowAdapter; @@ -31,6 +33,28 @@ pub(crate) fn from_runtime(o: core_layout::Orientation) -> Orientation { } } +pub(crate) fn compute_grid_layout_info( + grid_layout: &GridLayout, + organized_data: &GridLayoutOrganizedData, + orientation: Orientation, + local_context: &mut EvalLocalContext, +) -> Value { + let component = local_context.component_instance; + let expr_eval = |nr: &NamedReference| -> f32 { + eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap() + }; + let (padding, spacing) = padding_and_spacing(&grid_layout.geometry, orientation, &expr_eval); + let constraints = grid_layout_constraints(grid_layout, orientation, local_context); + core_layout::grid_layout_info( + organized_data.clone(), + Slice::from_slice(constraints.as_slice()), + spacing, + &padding, + to_runtime(orientation), + ) + .into() +} + pub(crate) fn compute_layout_info( lay: &Layout, orientation: Orientation, @@ -41,18 +65,8 @@ pub(crate) fn compute_layout_info( eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap() }; match lay { - Layout::GridLayout(grid_layout) => { - let cells = - grid_layout_data(grid_layout, orientation, component, &expr_eval, local_context); - let (padding, spacing) = - padding_and_spacing(&grid_layout.geometry, orientation, &expr_eval); - core_layout::grid_layout_info( - Slice::from(cells.as_slice()), - spacing, - &padding, - to_runtime(orientation), - ) - .into() + Layout::GridLayout(_) => { + panic!("only BoxLayout is supported"); } Layout::BoxLayout(box_layout) => { let (cells, alignment) = @@ -74,6 +88,44 @@ pub(crate) fn compute_layout_info( } } +pub(crate) fn organize_grid_layout( + layout: &GridLayout, + local_context: &mut EvalLocalContext, +) -> Value { + let cells = grid_layout_input_data(layout, local_context); + core_layout::organize_grid_layout(Slice::from_slice(cells.as_slice())).into() +} + +pub(crate) fn solve_grid_layout( + organized_data: &GridLayoutOrganizedData, + grid_layout: &GridLayout, + orientation: Orientation, + local_context: &mut EvalLocalContext, +) -> Value { + let component = local_context.component_instance; + let expr_eval = |nr: &NamedReference| -> f32 { + eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap() + }; + let constraints = grid_layout_constraints(grid_layout, orientation, local_context); + + let (padding, spacing) = padding_and_spacing(&grid_layout.geometry, orientation, &expr_eval); + let size_ref = grid_layout.geometry.rect.size_reference(orientation); + + let data = core_layout::GridLayoutData { + size: size_ref.map(expr_eval).unwrap_or(0.), + spacing, + padding, + organized_data: organized_data.clone(), + }; + + core_layout::solve_grid_layout( + &data, + Slice::from_slice(constraints.as_slice()), + to_runtime(orientation), + ) + .into() +} + pub(crate) fn solve_layout( lay: &Layout, orientation: Orientation, @@ -85,33 +137,8 @@ pub(crate) fn solve_layout( }; match lay { - Layout::GridLayout(grid_layout) => { - let mut cells = - grid_layout_data(grid_layout, orientation, component, &expr_eval, local_context); - if let (Some(buttons_roles), Orientation::Horizontal) = - (&grid_layout.dialog_button_roles, orientation) - { - let roles = buttons_roles - .iter() - .map(|r| DialogButtonRole::from_str(r).unwrap()) - .collect::>(); - core_layout::reorder_dialog_button_layout(&mut cells, &roles); - } - - let (padding, spacing) = - padding_and_spacing(&grid_layout.geometry, orientation, &expr_eval); - - let size_ref = grid_layout.geometry.rect.size_reference(orientation); - core_layout::solve_grid_layout( - &core_layout::GridLayoutData { - size: size_ref.map(expr_eval).unwrap_or(0.), - spacing, - padding, - cells: Slice::from(cells.as_slice()), - }, - to_runtime(orientation), - ) - .into() + Layout::GridLayout(_) => { + panic!("solve_layout called on GridLayout; use solve_grid_layout instead"); } Layout::BoxLayout(box_layout) => { let mut repeated_indices = Vec::new(); @@ -155,31 +182,14 @@ fn padding_and_spacing( (padding, spacing) } -/// return the celldata, the padding, and the spacing of a grid layout -fn grid_layout_data( +fn grid_layout_input_data( grid_layout: &i_slint_compiler::layout::GridLayout, - orientation: Orientation, - component: InstanceRef, - expr_eval: &impl Fn(&NamedReference) -> f32, local_context: &mut EvalLocalContext, -) -> Vec { - let mut cells = grid_layout +) -> Vec { + grid_layout .elems .iter() .map(|cell| { - let mut layout_info = get_layout_info( - &cell.item.element, - component, - &component.window_adapter(), - orientation, - ); - fill_layout_info_constraints( - &mut layout_info, - &cell.item.constraints, - orientation, - &expr_eval, - ); - let span = cell.span(orientation); let mut eval_or_default = |expr: &Option, default: u16| match expr { None => default, Some(e) => { @@ -195,21 +205,40 @@ fn grid_layout_data( }; let row = eval_or_default(&cell.row_expr, u16::MAX); let col = eval_or_default(&cell.col_expr, u16::MAX); - let span = eval_or_default(&span, 1); - core_layout::GridLayoutCellData { - new_row: cell.new_row, - col_or_row: match orientation { - Orientation::Horizontal => col, - Orientation::Vertical => row, - }, - row, - span, - constraint: layout_info, - } + let rowspan = eval_or_default(&cell.rowspan_expr, 1); + let colspan = eval_or_default(&cell.colspan_expr, 1); + core_layout::GridLayoutInputData { new_row: cell.new_row, col, row, colspan, rowspan } }) - .collect::>(); - core_layout::determine_row_col_numbers(&mut cells, to_runtime(orientation)); - cells + .collect() +} + +fn grid_layout_constraints( + grid_layout: &i_slint_compiler::layout::GridLayout, + orientation: Orientation, + ctx: &mut EvalLocalContext, +) -> Vec { + let component = ctx.component_instance; + let expr_eval = |nr: &NamedReference| -> f32 { + eval::load_property(component, &nr.element(), nr.name()).unwrap().try_into().unwrap() + }; + let mut constraints = Vec::with_capacity(grid_layout.elems.len()); + + for cell in grid_layout.elems.iter() { + let mut layout_info = get_layout_info( + &cell.item.element, + component, + &component.window_adapter(), + orientation, + ); + fill_layout_info_constraints( + &mut layout_info, + &cell.item.constraints, + orientation, + &expr_eval, + ); + constraints.push(layout_info); + } + constraints } fn box_layout_data( diff --git a/tests/cases/layout/grid_variable_row_col.slint b/tests/cases/layout/grid_variable_row_col.slint index 6155ecdec4..0808467b9e 100644 --- a/tests/cases/layout/grid_variable_row_col.slint +++ b/tests/cases/layout/grid_variable_row_col.slint @@ -29,6 +29,9 @@ export component TestCase inherits Window { width: 40phx; height: 40phx; } + txt := Text { + text: txt.row + "," + txt.col; + } rr := Rectangle { background: red; row: red_row; @@ -39,10 +42,12 @@ export component TestCase inherits Window { } } out property initial_grid_ok: { - // red is at (0, 0), green is at (row=0, col=1), blue is at (row=1, col=0) with colspan 2 + // red is at (0, 0), green is at (row=0, col=1), txt is at (row=0, col=2), + // and blue is at (row=1, col=0) with colspan 2 rr.x == 50phx && rr.y == 50phx && rr.colspan == 1 && rg.x == 100phx && rg.y == 50phx && rg.colspan == 1 && - rb.x == 50phx && rb.y == 100phx && rb.width == 90phx && rb.colspan == 2 + rb.x == 50phx && rb.y == 100phx && rb.width == 90phx && rb.colspan == 2 && + txt.text == "0,2" } public function change_grid() { @@ -54,9 +59,11 @@ export component TestCase inherits Window { out property final_grid_ok: { // now they are all at col 0, and in the order green, red, blue - rr.x == 50phx && rr.y == 100phx && rr.width == 40phx && rr.colspan == 1 && - rg.x == 50phx && rg.y == 50phx && rg.width == 40phx && rg.colspan == 1 && - rb.x == 50phx && rb.y == 150phx && rb.width == 40phx && rb.colspan == 1 + // and txt is at (row=0, col=1) + rr.x == 50phx && rr.y == 100phx && rr.width == 40phx && rr.row == 1 && rr.colspan == 1 && + rg.x == 50phx && rg.y == 50phx && rg.width == 40phx && rg.row == 0 && rg.colspan == 1 && + rb.x == 50phx && rb.y == 150phx && rb.width == 40phx && rb.row == 2 && rb.colspan == 1 && + txt.text == "0,1" } init => {