Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions api/cpp/include/slint.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,13 @@ inline SharedVector<float> solve_box_layout(const cbindgen_private::BoxLayoutDat
}

inline SharedVector<uint16_t>
organize_grid_layout(cbindgen_private::Slice<cbindgen_private::GridLayoutInputData> input_data)
organize_grid_layout(cbindgen_private::Slice<cbindgen_private::GridLayoutInputData> input_data,
cbindgen_private::Slice<int> repeater_indices)
{
SharedVector<uint16_t> result;
cbindgen_private::slint_organize_grid_layout(input_data, &result);
cbindgen_private::Slice<uint32_t> ri =
make_slice(reinterpret_cast<uint32_t *>(repeater_indices.ptr), repeater_indices.len);
cbindgen_private::slint_organize_grid_layout(input_data, ri, &result);
return result;
}

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

inline SharedVector<float>
solve_grid_layout(const cbindgen_private::GridLayoutData &data,
cbindgen_private::Slice<cbindgen_private::LayoutInfo> constraints,
cbindgen_private::Orientation orientation)
cbindgen_private::Slice<cbindgen_private::BoxLayoutCellData> constraints,
cbindgen_private::Orientation orientation,
cbindgen_private::Slice<int> repeater_indices)
{
SharedVector<float> result;
cbindgen_private::slint_solve_grid_layout(&data, constraints, orientation, &result);
cbindgen_private::Slice<uint32_t> ri =
make_slice(reinterpret_cast<uint32_t *>(repeater_indices.ptr), repeater_indices.len);
cbindgen_private::slint_solve_grid_layout(&data, constraints, orientation, ri, &result);
return result;
}

inline cbindgen_private::LayoutInfo
grid_layout_info(const cbindgen_private::GridLayoutOrganizedData &organized_data,
cbindgen_private::Slice<cbindgen_private::LayoutInfo> constraints, float spacing,
cbindgen_private::Slice<cbindgen_private::BoxLayoutCellData> constraints,
cbindgen_private::Slice<int> repeater_indices, float spacing,
const cbindgen_private::Padding &padding,
cbindgen_private::Orientation orientation)
{
return cbindgen_private::slint_grid_layout_info(&organized_data, constraints, spacing, &padding,
orientation);
cbindgen_private::Slice<uint32_t> ri =
make_slice(reinterpret_cast<uint32_t *>(repeater_indices.ptr), repeater_indices.len);
return cbindgen_private::slint_grid_layout_info(&organized_data, constraints, ri, spacing,
&padding, orientation);
}

inline cbindgen_private::LayoutInfo
Expand All @@ -176,9 +185,11 @@ box_layout_info_ortho(cbindgen_private::Slice<cbindgen_private::BoxLayoutCellDat
}

/// Access the layout cache of an item within a repeater
inline float layout_cache_access(const SharedVector<float> &cache, int offset, int repeater_index)
template<typename T>
inline T layout_cache_access(const SharedVector<T> &cache, int offset, int repeater_index,
int entries_per_item)
{
size_t idx = size_t(cache[offset]) + repeater_index * 2;
size_t idx = size_t(cache[offset]) + repeater_index * entries_per_item;
return idx < cache.size() ? cache[idx] : 0;
}

Expand Down
30 changes: 21 additions & 9 deletions internal/compiler/expression_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -752,11 +752,16 @@ pub enum Expression {
ReturnStatement(Option<Box<Expression>>),

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

/// Organize a grid layout, i.e. decide what goes where
Expand Down Expand Up @@ -1881,14 +1886,21 @@ pub fn pretty_print(f: &mut dyn std::fmt::Write, expression: &Expression) -> std
write!(f, "return ")?;
e.as_ref().map(|e| pretty_print(f, e)).unwrap_or(Ok(()))
}
Expression::LayoutCacheAccess { layout_cache_prop, index, repeater_index } => {
write!(
f,
"{:?}[{}{}]",
layout_cache_prop,
index,
if repeater_index.is_some() { " + $index" } else { "" }
)
Expression::LayoutCacheAccess {
layout_cache_prop,
index,
repeater_index,
entries_per_item,
} => {
if repeater_index.is_some() {
write!(
f,
"{:?}[{:?}[{}] + $repeater_index * {}]",
layout_cache_prop, layout_cache_prop, index, entries_per_item
)
} else {
write!(f, "{:?}[{}]", layout_cache_prop, index)
}
}
Expression::OrganizeGridLayout(..) => write!(f, "organize_grid_layout(..)"),
Expression::ComputeLayoutInfo(..) => write!(f, "layout_info(..)"),
Expand Down
101 changes: 98 additions & 3 deletions internal/compiler/generator/cpp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3716,15 +3716,21 @@ fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String
ident(&value.to_pascal_case()),
)
}
Expression::LayoutCacheAccess { layout_cache_prop, index, repeater_index } => {
Expression::LayoutCacheAccess {
layout_cache_prop,
index,
repeater_index,
entries_per_item,
} => {
let cache = access_member(layout_cache_prop, ctx);
cache.map_or_default(|cache| {
if let Some(ri) = repeater_index {
format!(
"slint::private_api::layout_cache_access({}.get(), {}, {})",
"slint::private_api::layout_cache_access({}.get(), {}, {}, {})",
cache,
index,
compile_expression(ri, ctx)
compile_expression(ri, ctx),
entries_per_item
)
} else {
format!("{cache}.get()[{index}]")
Expand All @@ -3745,6 +3751,18 @@ fn compile_expression(expr: &llr::Expression, ctx: &EvaluationContext) -> String
sub_expression,
ctx,
),
Expression::GridInputFunction {
cells_variable,
repeater_indices,
elements,
sub_expression,
} => grid_input_function(
cells_variable,
repeater_indices.as_ref().map(SmolStr::as_str),
elements.as_ref(),
sub_expression,
ctx,
),
Expression::MinMax { ty, op, lhs, rhs } => {
let ident = match op {
MinMaxOp::Min => "min",
Expand Down Expand Up @@ -4373,6 +4391,83 @@ fn box_layout_function(
)
}

fn grid_input_function(
cells_variable: &str,
repeated_indices: Option<&str>,
elements: &[Either<llr::Expression, llr::GridLayoutRepeatedElement>],
sub_expression: &llr::Expression,
ctx: &llr_EvaluationContext<CppGeneratorContext>,
) -> String {
let mut push_code =
"std::vector<slint::cbindgen_private::GridLayoutInputData> cells_vector;".to_owned();
let mut repeater_idx = 0usize;
let mut has_new_row_bool = false;

for item in elements {
match item {
Either::Left(value) => {
write!(
push_code,
"cells_vector.push_back({{ {} }});",
compile_expression(value, ctx)
)
.unwrap();
}
Either::Right(repeater) => {
let repeater_id = format!("repeater_{}", usize::from(repeater.repeater_index));
write!(push_code, "self->{repeater_id}.ensure_updated(self);").unwrap();

if let Some(ri) = &repeated_indices {
write!(push_code, "{}_array[{}] = cells_vector.size();", ri, repeater_idx * 2)
.unwrap();
write!(
push_code,
"{ri}_array[{c}] = self->{id}.len();",
ri = ri,
c = repeater_idx * 2 + 1,
id = repeater_id,
)
.unwrap();
}
repeater_idx += 1;
write!(
push_code,
"{maybe_bool} new_row = {new_row};
self->{id}.for_each([&](const auto &/*sub_comp*/) {{
cells_vector.push_back({{ new_row, {col}, {row}, {colspan}, {rowspan} }});
new_row = false;
}});",
new_row = repeater.new_row,
maybe_bool = if has_new_row_bool { "" } else { "bool " },
id = repeater_id,
col = u16::MAX,
colspan = 1,
row = u16::MAX,
rowspan = 1,
)
.unwrap();
has_new_row_bool = true;
}
}
}

let ri = repeated_indices.as_ref().map_or(String::new(), |ri| {
write!(
push_code,
"slint::cbindgen_private::Slice<int> {ri} = slint::private_api::make_slice(std::span({ri}_array));"
)
.unwrap();
format!("std::array<int, {}> {}_array;", 2 * repeater_idx, ri)
});
format!(
"[&]{{ {} {} slint::cbindgen_private::Slice<slint::cbindgen_private::GridLayoutInputData>{} = slint::private_api::make_slice(std::span(cells_vector)); return {}; }}()",
ri,
push_code,
ident(cells_variable),
compile_expression(sub_expression, ctx)
)
}

/// Like compile expression, but prepended with `return` if not void.
/// ret_type is the expecting type that should be returned with that return statement
fn return_compile_expression(
Expand Down
90 changes: 88 additions & 2 deletions internal/compiler/generator/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2693,13 +2693,13 @@ fn compile_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream
quote!(sp::#base_ident::#value_ident)
}
}
Expression::LayoutCacheAccess { layout_cache_prop, index, repeater_index } => {
Expression::LayoutCacheAccess { layout_cache_prop, index, repeater_index, entries_per_item } => {
access_member(layout_cache_prop, ctx).map_or_default(|cache| {
if let Some(ri) = repeater_index {
let offset = compile_expression(ri, ctx);
quote!({
let cache = #cache.get();
*cache.get((cache[#index] as usize) + #offset as usize * 2).unwrap_or(&(0 as sp::Coord))
*cache.get((cache[#index] as usize) + #offset as usize * #entries_per_item).unwrap_or(&(0 as _))
})
} else {
quote!(#cache.get()[#index])
Expand All @@ -2720,6 +2720,17 @@ fn compile_expression(expr: &Expression, ctx: &EvaluationContext) -> TokenStream
sub_expression,
ctx,
),

Expression::GridInputFunction {
cells_variable, repeater_indices, elements, sub_expression
} => grid_input_function(
cells_variable,
repeater_indices.as_ref().map(SmolStr::as_str),
elements.as_ref(),
sub_expression,
ctx,
),

Expression::MinMax { ty, op, lhs, rhs } => {
let lhs = compile_expression(lhs, ctx);
let t = rust_primitive_type(ty);
Expand Down Expand Up @@ -3420,6 +3431,81 @@ fn struct_name_to_tokens(name: &StructName) -> Option<proc_macro2::TokenStream>
}
}

fn grid_input_function(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you did consider trying to merge this with box_layout_function, and that wasn't worth it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You guess right ;)

  • The input arguments differ
    ** box layout wants an orientation, grid doesn't
    ** for each repeater, boxlayout only needs the repeater number, grid also needs the new_row value (and I'm guessing we'll need more information for the next feature, repeating Rows), which is why the second type in the Either has changed
  • the code being pushed for each instance is different obviously, including a variable outside the for loop, for grids -- I'm not sure how this could be passed as parameter given that stuff inside quote! refers to local variables outside of it, coming from the shared logic

Maybe instead of a single function with a ton of parameters, there could be shared helpers between the two? The last 13 lines are identical, for instance. I can look into factorizing that if you think it's a good idea (however it'll be a function with 7 variables, AFAICS: repeater_idx, cells_variable, sub_expression, fixed_count, repeated_count, push_code)

Or maybe this is a job for a macro? It can provide the beginning and end, and let the two functions implement the inner stuff differently. But the difference in type for repeater makes this tricky.

cells_variable: &str,
repeated_indices: Option<&str>,
elements: &[Either<Expression, llr::GridLayoutRepeatedElement>],
sub_expression: &Expression,
ctx: &EvaluationContext,
) -> TokenStream {
let repeated_indices = repeated_indices.map(ident);
let inner_component_id = self::inner_component_id(ctx.current_sub_component().unwrap());
let mut fixed_count = 0usize;
let mut repeated_count = quote!();
let mut push_code = Vec::new();
let mut repeater_idx = 0usize;
for item in elements {
match item {
Either::Left(value) => {
let value = compile_expression(value, ctx);
fixed_count += 1;
push_code.push(quote!(items_vec.push(#value);))
}
Either::Right(repeater) => {
let repeater_id = format_ident!("repeater{}", usize::from(repeater.repeater_index));
let rep_inner_component_id = self::inner_component_id(
&ctx.compilation_unit.sub_components[ctx
.current_sub_component()
.unwrap()
.repeated[repeater.repeater_index]
.sub_tree
.root],
);
repeated_count = quote!(#repeated_count + _self.#repeater_id.len());
let ri = repeated_indices.as_ref().map(|ri| {
quote!(
#ri[#repeater_idx * 2] = items_vec.len() as u32;
#ri[#repeater_idx * 2 + 1] = internal_vec.len() as u32;
)
});
repeater_idx += 1;
let new_row = repeater.new_row;
push_code.push(quote!(
#inner_component_id::FIELD_OFFSETS.#repeater_id.apply_pin(_self).ensure_updated(
|| { #rep_inner_component_id::new(_self.self_weak.get().unwrap().clone()).unwrap().into() }
);
let internal_vec = _self.#repeater_id.instances_vec();
#ri
let mut new_row = #new_row;
for _sub_comp in &internal_vec {
items_vec.push(sp::GridLayoutInputData {
r#col: u16::MAX as _,
r#colspan: 1 as _,
r#new_row,
r#row: u16::MAX as _,
r#rowspan: 1 as _,
});
new_row = false;
}
));
}
}
}
let ri = repeated_indices.as_ref().map(|ri| quote!(let mut #ri = [0u32; 2 * #repeater_idx];));
let ri2 = repeated_indices.map(|ri| quote!(let #ri = sp::Slice::from_slice(&#ri);));
let cells_variable = ident(cells_variable);
let sub_expression = compile_expression(sub_expression, ctx);

quote! { {
#ri
let mut items_vec = sp::Vec::with_capacity(#fixed_count #repeated_count);
#(#push_code)*
let #cells_variable = sp::Slice::from_slice(&items_vec);
#ri2
#sub_expression
} }
}

fn box_layout_function(
cells_variable: &str,
repeated_indices: Option<&str>,
Expand Down
Loading
Loading