Skip to content

Commit 3b410f7

Browse files
committed
Implement support for models in the Row of a GridLayout
This is only part of the overall if/for support in grid: for now it's only supported to repeat cells in a given row, one cannot repeat entire Rows this way yet. Some implementation notes: - GridLayoutOrganizedData (4 u16s per cell) can now contain indirection indices in the case of repeaters, like LayoutCache (2 f32s per cell). The "generator" for that is separate code due to these two differences (amount and type), while LayoutCacheAccess can be used for both (it now receives the additional information of how many entries per item, as a parameter) - BoxLayoutFunction and BoxLayoutCellData are used by the grid layouting code too; good for code sharing, but bad naming then. There are TODOs to rename those in a followup commit. This code reuse required porting from LayoutInfo to BoxLayoutCellData (which has only one field, the LayoutInfo, I suppose this is good for future extensions...). - Compared to handling of repeaters for box layouts, there are additional complications due to the "new_row" bool, especially with empty repeaters (the first item at compile time isn't necessarily the first item at runtime). The generated code solves that with a "running" new_row bool, to detect if the repeater had instances or not.
1 parent 6d08036 commit 3b410f7

File tree

19 files changed

+1121
-250
lines changed

19 files changed

+1121
-250
lines changed

api/cpp/include/slint.h

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,13 @@ inline SharedVector<float> solve_box_layout(const cbindgen_private::BoxLayoutDat
124124
}
125125

126126
inline SharedVector<uint16_t>
127-
organize_grid_layout(cbindgen_private::Slice<cbindgen_private::GridLayoutInputData> input_data)
127+
organize_grid_layout(cbindgen_private::Slice<cbindgen_private::GridLayoutInputData> input_data,
128+
cbindgen_private::Slice<int> repeater_indices)
128129
{
129130
SharedVector<uint16_t> result;
130-
cbindgen_private::slint_organize_grid_layout(input_data, &result);
131+
cbindgen_private::Slice<uint32_t> ri =
132+
make_slice(reinterpret_cast<uint32_t *>(repeater_indices.ptr), repeater_indices.len);
133+
cbindgen_private::slint_organize_grid_layout(input_data, ri, &result);
131134
return result;
132135
}
133136

@@ -142,22 +145,28 @@ inline SharedVector<uint16_t> organize_dialog_button_layout(
142145

143146
inline SharedVector<float>
144147
solve_grid_layout(const cbindgen_private::GridLayoutData &data,
145-
cbindgen_private::Slice<cbindgen_private::LayoutInfo> constraints,
146-
cbindgen_private::Orientation orientation)
148+
cbindgen_private::Slice<cbindgen_private::BoxLayoutCellData> constraints,
149+
cbindgen_private::Orientation orientation,
150+
cbindgen_private::Slice<int> repeater_indices)
147151
{
148152
SharedVector<float> result;
149-
cbindgen_private::slint_solve_grid_layout(&data, constraints, orientation, &result);
153+
cbindgen_private::Slice<uint32_t> ri =
154+
make_slice(reinterpret_cast<uint32_t *>(repeater_indices.ptr), repeater_indices.len);
155+
cbindgen_private::slint_solve_grid_layout(&data, constraints, orientation, ri, &result);
150156
return result;
151157
}
152158

153159
inline cbindgen_private::LayoutInfo
154160
grid_layout_info(const cbindgen_private::GridLayoutOrganizedData &organized_data,
155-
cbindgen_private::Slice<cbindgen_private::LayoutInfo> constraints, float spacing,
161+
cbindgen_private::Slice<cbindgen_private::BoxLayoutCellData> constraints,
162+
cbindgen_private::Slice<int> repeater_indices, float spacing,
156163
const cbindgen_private::Padding &padding,
157164
cbindgen_private::Orientation orientation)
158165
{
159-
return cbindgen_private::slint_grid_layout_info(&organized_data, constraints, spacing, &padding,
160-
orientation);
166+
cbindgen_private::Slice<uint32_t> ri =
167+
make_slice(reinterpret_cast<uint32_t *>(repeater_indices.ptr), repeater_indices.len);
168+
return cbindgen_private::slint_grid_layout_info(&organized_data, constraints, ri, spacing,
169+
&padding, orientation);
161170
}
162171

163172
inline cbindgen_private::LayoutInfo
@@ -176,9 +185,11 @@ box_layout_info_ortho(cbindgen_private::Slice<cbindgen_private::BoxLayoutCellDat
176185
}
177186

178187
/// Access the layout cache of an item within a repeater
179-
inline float layout_cache_access(const SharedVector<float> &cache, int offset, int repeater_index)
188+
template<typename T>
189+
inline T layout_cache_access(const SharedVector<T> &cache, int offset, int repeater_index,
190+
int entries_per_item)
180191
{
181-
size_t idx = size_t(cache[offset]) + repeater_index * 2;
192+
size_t idx = size_t(cache[offset]) + repeater_index * entries_per_item;
182193
return idx < cache.size() ? cache[idx] : 0;
183194
}
184195

internal/compiler/expression_tree.rs

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -752,11 +752,16 @@ pub enum Expression {
752752
ReturnStatement(Option<Box<Expression>>),
753753

754754
LayoutCacheAccess {
755+
/// This property holds an array of entries
755756
layout_cache_prop: NamedReference,
757+
/// The index into that array. If repeater_index is None, then the code will be layout_cache_prop[index]
756758
index: usize,
757759
/// When set, this is the index within a repeater, and the index is then the location of another offset.
758-
/// So this looks like `layout_cache_prop[layout_cache_prop[index] + repeater_index]`
760+
/// So this looks like `layout_cache_prop[layout_cache_prop[index] + repeater_index * entries_per_item]`
759761
repeater_index: Option<Box<Expression>>,
762+
/// The number of entries per item (2 for LayoutCache, 4 for GridLayoutInputData)
763+
/// This is only used when repeater_index is set
764+
entries_per_item: usize,
760765
},
761766

762767
/// Organize a grid layout, i.e. decide what goes where
@@ -1881,14 +1886,21 @@ pub fn pretty_print(f: &mut dyn std::fmt::Write, expression: &Expression) -> std
18811886
write!(f, "return ")?;
18821887
e.as_ref().map(|e| pretty_print(f, e)).unwrap_or(Ok(()))
18831888
}
1884-
Expression::LayoutCacheAccess { layout_cache_prop, index, repeater_index } => {
1885-
write!(
1886-
f,
1887-
"{:?}[{}{}]",
1888-
layout_cache_prop,
1889-
index,
1890-
if repeater_index.is_some() { " + $index" } else { "" }
1891-
)
1889+
Expression::LayoutCacheAccess {
1890+
layout_cache_prop,
1891+
index,
1892+
repeater_index,
1893+
entries_per_item,
1894+
} => {
1895+
if repeater_index.is_some() {
1896+
write!(
1897+
f,
1898+
"{:?}[{:?}[{}] + $repeater_index * {}]",
1899+
layout_cache_prop, layout_cache_prop, index, entries_per_item
1900+
)
1901+
} else {
1902+
write!(f, "{:?}[{}]", layout_cache_prop, index)
1903+
}
18921904
}
18931905
Expression::OrganizeGridLayout(..) => write!(f, "organize_grid_layout(..)"),
18941906
Expression::ComputeLayoutInfo(..) => write!(f, "layout_info(..)"),

internal/compiler/generator/cpp.rs

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3716,15 +3716,21 @@ fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String
37163716
ident(&value.to_pascal_case()),
37173717
)
37183718
}
3719-
Expression::LayoutCacheAccess { layout_cache_prop, index, repeater_index } => {
3719+
Expression::LayoutCacheAccess {
3720+
layout_cache_prop,
3721+
index,
3722+
repeater_index,
3723+
entries_per_item,
3724+
} => {
37203725
let cache = access_member(layout_cache_prop, ctx);
37213726
cache.map_or_default(|cache| {
37223727
if let Some(ri) = repeater_index {
37233728
format!(
3724-
"slint::private_api::layout_cache_access({}.get(), {}, {})",
3729+
"slint::private_api::layout_cache_access({}.get(), {}, {}, {})",
37253730
cache,
37263731
index,
3727-
compile_expression(ri, ctx)
3732+
compile_expression(ri, ctx),
3733+
entries_per_item
37283734
)
37293735
} else {
37303736
format!("{cache}.get()[{index}]")
@@ -3745,6 +3751,18 @@ fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String
37453751
sub_expression,
37463752
ctx,
37473753
),
3754+
Expression::GridInputFunction {
3755+
cells_variable,
3756+
repeater_indices,
3757+
elements,
3758+
sub_expression,
3759+
} => grid_input_function(
3760+
cells_variable,
3761+
repeater_indices.as_ref().map(SmolStr::as_str),
3762+
elements.as_ref(),
3763+
sub_expression,
3764+
ctx,
3765+
),
37483766
Expression::MinMax { ty, op, lhs, rhs } => {
37493767
let ident = match op {
37503768
MinMaxOp::Min => "min",
@@ -4373,6 +4391,83 @@ fn box_layout_function(
43734391
)
43744392
}
43754393

4394+
fn grid_input_function(
4395+
cells_variable: &str,
4396+
repeated_indices: Option<&str>,
4397+
elements: &[Either<llr::Expression, llr::GridLayoutRepeatedElement>],
4398+
sub_expression: &llr::Expression,
4399+
ctx: &llr_EvaluationContext<CppGeneratorContext>,
4400+
) -> String {
4401+
let mut push_code =
4402+
"std::vector<slint::cbindgen_private::GridLayoutInputData> cells_vector;".to_owned();
4403+
let mut repeater_idx = 0usize;
4404+
let mut has_new_row_bool = false;
4405+
4406+
for item in elements {
4407+
match item {
4408+
Either::Left(value) => {
4409+
write!(
4410+
push_code,
4411+
"cells_vector.push_back({{ {} }});",
4412+
compile_expression(value, ctx)
4413+
)
4414+
.unwrap();
4415+
}
4416+
Either::Right(repeater) => {
4417+
let repeater_id = format!("repeater_{}", usize::from(repeater.repeater_index));
4418+
write!(push_code, "self->{repeater_id}.ensure_updated(self);").unwrap();
4419+
4420+
if let Some(ri) = &repeated_indices {
4421+
write!(push_code, "{}_array[{}] = cells_vector.size();", ri, repeater_idx * 2)
4422+
.unwrap();
4423+
write!(
4424+
push_code,
4425+
"{ri}_array[{c}] = self->{id}.len();",
4426+
ri = ri,
4427+
c = repeater_idx * 2 + 1,
4428+
id = repeater_id,
4429+
)
4430+
.unwrap();
4431+
}
4432+
repeater_idx += 1;
4433+
write!(
4434+
push_code,
4435+
"{maybe_bool} new_row = {new_row};
4436+
self->{id}.for_each([&](const auto &/*sub_comp*/) {{
4437+
cells_vector.push_back({{ new_row, {col}, {row}, {colspan}, {rowspan} }});
4438+
new_row = false;
4439+
}});",
4440+
new_row = repeater.new_row,
4441+
maybe_bool = if has_new_row_bool { "" } else { "bool " },
4442+
id = repeater_id,
4443+
col = u16::MAX,
4444+
colspan = 1,
4445+
row = u16::MAX,
4446+
rowspan = 1,
4447+
)
4448+
.unwrap();
4449+
has_new_row_bool = true;
4450+
}
4451+
}
4452+
}
4453+
4454+
let ri = repeated_indices.as_ref().map_or(String::new(), |ri| {
4455+
write!(
4456+
push_code,
4457+
"slint::cbindgen_private::Slice<int> {ri} = slint::private_api::make_slice(std::span({ri}_array));"
4458+
)
4459+
.unwrap();
4460+
format!("std::array<int, {}> {}_array;", 2 * repeater_idx, ri)
4461+
});
4462+
format!(
4463+
"[&]{{ {} {} slint::cbindgen_private::Slice<slint::cbindgen_private::GridLayoutInputData>{} = slint::private_api::make_slice(std::span(cells_vector)); return {}; }}()",
4464+
ri,
4465+
push_code,
4466+
ident(cells_variable),
4467+
compile_expression(sub_expression, ctx)
4468+
)
4469+
}
4470+
43764471
/// Like compile expression, but prepended with `return` if not void.
43774472
/// ret_type is the expecting type that should be returned with that return statement
43784473
fn return_compile_expression(

internal/compiler/generator/rust.rs

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2693,13 +2693,13 @@ fn compile_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream
26932693
quote!(sp::#base_ident::#value_ident)
26942694
}
26952695
}
2696-
Expression::LayoutCacheAccess { layout_cache_prop, index, repeater_index } => {
2696+
Expression::LayoutCacheAccess { layout_cache_prop, index, repeater_index, entries_per_item } => {
26972697
access_member(layout_cache_prop, ctx).map_or_default(|cache| {
26982698
if let Some(ri) = repeater_index {
26992699
let offset = compile_expression(ri, ctx);
27002700
quote!({
27012701
let cache = #cache.get();
2702-
*cache.get((cache[#index] as usize) + #offset as usize * 2).unwrap_or(&(0 as sp::Coord))
2702+
*cache.get((cache[#index] as usize) + #offset as usize * #entries_per_item).unwrap_or(&(0 as _))
27032703
})
27042704
} else {
27052705
quote!(#cache.get()[#index])
@@ -2720,6 +2720,17 @@ fn compile_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream
27202720
sub_expression,
27212721
ctx,
27222722
),
2723+
2724+
Expression::GridInputFunction {
2725+
cells_variable, repeater_indices, elements, sub_expression
2726+
} => grid_input_function(
2727+
cells_variable,
2728+
repeater_indices.as_ref().map(SmolStr::as_str),
2729+
elements.as_ref(),
2730+
sub_expression,
2731+
ctx,
2732+
),
2733+
27232734
Expression::MinMax { ty, op, lhs, rhs } => {
27242735
let lhs = compile_expression(lhs, ctx);
27252736
let t = rust_primitive_type(ty);
@@ -3420,6 +3431,81 @@ fn struct_name_to_tokens(name: &StructName) -> Option<proc_macro2::TokenStream>
34203431
}
34213432
}
34223433

3434+
fn grid_input_function(
3435+
cells_variable: &str,
3436+
repeated_indices: Option<&str>,
3437+
elements: &[Either<Expression, llr::GridLayoutRepeatedElement>],
3438+
sub_expression: &Expression,
3439+
ctx: &EvaluationContext,
3440+
) -> TokenStream {
3441+
let repeated_indices = repeated_indices.map(ident);
3442+
let inner_component_id = self::inner_component_id(ctx.current_sub_component().unwrap());
3443+
let mut fixed_count = 0usize;
3444+
let mut repeated_count = quote!();
3445+
let mut push_code = Vec::new();
3446+
let mut repeater_idx = 0usize;
3447+
for item in elements {
3448+
match item {
3449+
Either::Left(value) => {
3450+
let value = compile_expression(value, ctx);
3451+
fixed_count += 1;
3452+
push_code.push(quote!(items_vec.push(#value);))
3453+
}
3454+
Either::Right(repeater) => {
3455+
let repeater_id = format_ident!("repeater{}", usize::from(repeater.repeater_index));
3456+
let rep_inner_component_id = self::inner_component_id(
3457+
&ctx.compilation_unit.sub_components[ctx
3458+
.current_sub_component()
3459+
.unwrap()
3460+
.repeated[repeater.repeater_index]
3461+
.sub_tree
3462+
.root],
3463+
);
3464+
repeated_count = quote!(#repeated_count + _self.#repeater_id.len());
3465+
let ri = repeated_indices.as_ref().map(|ri| {
3466+
quote!(
3467+
#ri[#repeater_idx * 2] = items_vec.len() as u32;
3468+
#ri[#repeater_idx * 2 + 1] = internal_vec.len() as u32;
3469+
)
3470+
});
3471+
repeater_idx += 1;
3472+
let new_row = repeater.new_row;
3473+
push_code.push(quote!(
3474+
#inner_component_id::FIELD_OFFSETS.#repeater_id.apply_pin(_self).ensure_updated(
3475+
|| { #rep_inner_component_id::new(_self.self_weak.get().unwrap().clone()).unwrap().into() }
3476+
);
3477+
let internal_vec = _self.#repeater_id.instances_vec();
3478+
#ri
3479+
let mut new_row = #new_row;
3480+
for _sub_comp in &internal_vec {
3481+
items_vec.push(sp::GridLayoutInputData {
3482+
r#col: u16::MAX as _,
3483+
r#colspan: 1 as _,
3484+
r#new_row,
3485+
r#row: u16::MAX as _,
3486+
r#rowspan: 1 as _,
3487+
});
3488+
new_row = false;
3489+
}
3490+
));
3491+
}
3492+
}
3493+
}
3494+
let ri = repeated_indices.as_ref().map(|ri| quote!(let mut #ri = [0u32; 2 * #repeater_idx];));
3495+
let ri2 = repeated_indices.map(|ri| quote!(let #ri = sp::Slice::from_slice(&#ri);));
3496+
let cells_variable = ident(cells_variable);
3497+
let sub_expression = compile_expression(sub_expression, ctx);
3498+
3499+
quote! { {
3500+
#ri
3501+
let mut items_vec = sp::Vec::with_capacity(#fixed_count #repeated_count);
3502+
#(#push_code)*
3503+
let #cells_variable = sp::Slice::from_slice(&items_vec);
3504+
#ri2
3505+
#sub_expression
3506+
} }
3507+
}
3508+
34233509
fn box_layout_function(
34243510
cells_variable: &str,
34253511
repeated_indices: Option<&str>,

0 commit comments

Comments
 (0)