Skip to content

Commit 2037e30

Browse files
committed
Lay out grids at runtime rather than at compile time
This opens the door for using computed values in "row"/"col" properties, and later on for dynamic grid layouts (for/if support)
1 parent a6513ff commit 2037e30

File tree

9 files changed

+289
-110
lines changed

9 files changed

+289
-110
lines changed

api/cpp/include/slint.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,18 +123,18 @@ inline SharedVector<float> solve_box_layout(const cbindgen_private::BoxLayoutDat
123123
return result;
124124
}
125125

126-
inline SharedVector<float> solve_grid_layout(const cbindgen_private::GridLayoutData &data)
126+
inline SharedVector<float> solve_grid_layout(const cbindgen_private::GridLayoutData &data, cbindgen_private::Orientation orientation)
127127
{
128128
SharedVector<float> result;
129-
cbindgen_private::slint_solve_grid_layout(&data, &result);
129+
cbindgen_private::slint_solve_grid_layout(&data, orientation, &result);
130130
return result;
131131
}
132132

133133
inline cbindgen_private::LayoutInfo
134134
grid_layout_info(cbindgen_private::Slice<cbindgen_private::GridLayoutCellData> cells, float spacing,
135-
const cbindgen_private::Padding &padding)
135+
const cbindgen_private::Padding &padding, cbindgen_private::Orientation o)
136136
{
137-
return cbindgen_private::slint_grid_layout_info(cells, spacing, &padding);
137+
return cbindgen_private::slint_grid_layout_info(cells, spacing, &padding, o);
138138
}
139139

140140
inline cbindgen_private::LayoutInfo

internal/compiler/layout.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -278,18 +278,19 @@ impl LayoutConstraints {
278278
/// An element in a GridLayout
279279
#[derive(Debug, Clone)]
280280
pub struct GridLayoutElement {
281-
pub col: u16,
282-
pub row: u16,
281+
pub new_row: bool,
282+
pub col_expr: Option<Expression>,
283+
pub row_expr: Option<Expression>,
283284
pub colspan: u16,
284285
pub rowspan: u16,
285286
pub item: LayoutItem,
286287
}
287288

288289
impl GridLayoutElement {
289-
pub fn col_or_row_and_span(&self, orientation: Orientation) -> (u16, u16) {
290+
pub fn span(&self, orientation: Orientation) -> u16 {
290291
match orientation {
291-
Orientation::Horizontal => (self.col, self.colspan),
292-
Orientation::Vertical => (self.row, self.rowspan),
292+
Orientation::Horizontal => self.colspan,
293+
Orientation::Vertical => self.rowspan,
293294
}
294295
}
295296
}

internal/compiler/llr/expression.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,10 @@ impl<T> TypeResolutionContext for EvaluationContext<'_, T> {
730730
&self.compilation_unit.sub_components[sub_component.sub_components[*i].ty];
731731
}
732732

733-
sub_component.items[*item_index].ty.lookup_property(prop_name).unwrap()
733+
sub_component.items[*item_index]
734+
.ty
735+
.lookup_property(prop_name)
736+
.unwrap_or_else(|| panic!("property not found: {}", prop_name))
734737
}
735738
PropertyReference::InParent { level, parent_reference } => {
736739
let mut ctx = self;

internal/compiler/llr/lower_expression.rs

Lines changed: 61 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -597,9 +597,13 @@ fn compute_layout_info(
597597
crate::layout::Layout::GridLayout(layout) => {
598598
let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
599599
let cells = grid_layout_cell_data(layout, o, ctx);
600+
let orientation_literal = llr_Expression::EnumerationValue(EnumerationValue {
601+
value: o as _,
602+
enumeration: crate::typeregister::BUILTIN.with(|b| b.enums.Orientation.clone()),
603+
});
600604
llr_Expression::ExtraBuiltinFunctionCall {
601605
function: "grid_layout_info".into(),
602-
arguments: vec![cells, spacing, padding],
606+
arguments: vec![cells, spacing, padding, orientation_literal],
603607
return_ty: crate::typeregister::layout_info_type().into(),
604608
}
605609
}
@@ -643,6 +647,10 @@ fn solve_layout(
643647
let (padding, spacing) = generate_layout_padding_and_spacing(&layout.geometry, o, ctx);
644648
let cells = grid_layout_cell_data(layout, o, ctx);
645649
let size = layout_geometry_size(&layout.geometry.rect, o, ctx);
650+
let orientation_expr = llr_Expression::EnumerationValue(EnumerationValue {
651+
value: o as _,
652+
enumeration: crate::typeregister::BUILTIN.with(|b| b.enums.Orientation.clone()),
653+
});
646654
if let (Some(button_roles), Orientation::Horizontal) = (&layout.dialog_button_roles, o)
647655
{
648656
let cells_ty = cells.ty(ctx);
@@ -669,37 +677,43 @@ fn solve_layout(
669677
},
670678
llr_Expression::ExtraBuiltinFunctionCall {
671679
function: "solve_grid_layout".into(),
672-
arguments: vec![make_struct(
673-
"GridLayoutData",
674-
[
675-
("size", Type::Float32, size),
676-
("spacing", Type::Float32, spacing),
677-
("padding", padding.ty(ctx), padding),
678-
(
679-
"cells",
680-
cells_ty.clone(),
681-
llr_Expression::ReadLocalVariable {
682-
name: "cells".into(),
683-
ty: cells_ty,
684-
},
685-
),
686-
],
687-
)],
680+
arguments: vec![
681+
make_struct(
682+
"GridLayoutData",
683+
[
684+
("size", Type::Float32, size),
685+
("spacing", Type::Float32, spacing),
686+
("padding", padding.ty(ctx), padding),
687+
(
688+
"cells",
689+
cells_ty.clone(),
690+
llr_Expression::ReadLocalVariable {
691+
name: "cells".into(),
692+
ty: cells_ty,
693+
},
694+
),
695+
],
696+
),
697+
orientation_expr,
698+
],
688699
return_ty: Type::LayoutCache,
689700
},
690701
])
691702
} else {
692703
llr_Expression::ExtraBuiltinFunctionCall {
693704
function: "solve_grid_layout".into(),
694-
arguments: vec![make_struct(
695-
"GridLayoutData",
696-
[
697-
("size", Type::Float32, size),
698-
("spacing", Type::Float32, spacing),
699-
("padding", padding.ty(ctx), padding),
700-
("cells", cells.ty(ctx), cells),
701-
],
702-
)],
705+
arguments: vec![
706+
make_struct(
707+
"GridLayoutData",
708+
[
709+
("size", Type::Float32, size),
710+
("spacing", Type::Float32, spacing),
711+
("padding", padding.ty(ctx), padding),
712+
("cells", cells.ty(ctx), cells),
713+
],
714+
),
715+
orientation_expr,
716+
],
703717
return_ty: Type::LayoutCache,
704718
}
705719
}
@@ -846,15 +860,33 @@ fn grid_layout_cell_data(
846860
.elems
847861
.iter()
848862
.map(|c| {
849-
let (col_or_row, span) = c.col_or_row_and_span(orientation);
863+
let span = c.span(orientation);
850864
let layout_info =
851865
get_layout_info(&c.item.element, ctx, &c.item.constraints, orientation);
852866

867+
let mut lower_expr_or_auto = |expr: &Option<crate::expression_tree::Expression>| {
868+
expr.as_ref().map_or_else(
869+
|| llr_Expression::NumberLiteral(u16::MAX.into()), // MAX means "auto", see to_layout_data()
870+
|e| lower_expression(e, ctx),
871+
)
872+
};
873+
let row_expr = lower_expr_or_auto(&c.row_expr);
874+
let col_expr = lower_expr_or_auto(&c.col_expr);
875+
853876
make_struct(
854877
"GridLayoutCellData",
855878
[
856879
("constraint", crate::typeregister::layout_info_type().into(), layout_info),
857-
("col_or_row", Type::Int32, llr_Expression::NumberLiteral(col_or_row as _)),
880+
("new_row", Type::Bool, llr_Expression::BoolLiteral(c.new_row)),
881+
(
882+
"col_or_row",
883+
Type::Int32,
884+
match orientation {
885+
Orientation::Horizontal => col_expr,
886+
Orientation::Vertical => row_expr.clone(),
887+
},
888+
),
889+
("row", Type::Int32, row_expr),
858890
("span", Type::Int32, llr_Expression::NumberLiteral(span as _)),
859891
],
860892
)
@@ -867,6 +899,7 @@ fn grid_layout_cell_data(
867899
pub(super) fn grid_layout_cell_data_ty() -> Type {
868900
Type::Struct(Rc::new(Struct {
869901
fields: IntoIterator::into_iter([
902+
(SmolStr::new_static("new_row"), Type::Bool),
870903
(SmolStr::new_static("col_or_row"), Type::Int32),
871904
(SmolStr::new_static("span"), Type::Int32),
872905
(SmolStr::new_static("constraint"), crate::typeregister::layout_info_type().into()),

internal/compiler/passes/lower_layout.rs

Lines changed: 81 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -176,38 +176,24 @@ fn lower_grid_layout(
176176
layout_info_type().into(),
177177
);
178178

179-
let mut row = 0;
180-
let mut col = 0;
181-
182179
let layout_children = std::mem::take(&mut grid_layout_element.borrow_mut().children);
183180
let mut collected_children = Vec::new();
181+
let mut new_row = false; // true until the first child of a Row, or the first item after an empty Row
184182
for layout_child in layout_children {
185183
let is_row = if let ElementType::Builtin(be) = &layout_child.borrow().base_type {
186184
be.name == "Row"
187185
} else {
188186
false
189187
};
190188
if is_row {
191-
if col > 0 {
192-
row += 1;
193-
col = 0;
194-
}
189+
new_row = true;
195190
let row_children = std::mem::take(&mut layout_child.borrow_mut().children);
196191
for x in row_children {
197-
grid.add_element(
198-
&x,
199-
(&mut row, &mut col),
200-
&layout_cache_prop_h,
201-
&layout_cache_prop_v,
202-
diag,
203-
);
204-
col += 1;
192+
grid.add_element(&x, new_row, &layout_cache_prop_h, &layout_cache_prop_v, diag);
205193
collected_children.push(x);
194+
new_row = false;
206195
}
207-
if col > 0 {
208-
row += 1;
209-
col = 0;
210-
}
196+
new_row = true; // the end of a Row means the next item is the first of a new row
211197
if layout_child.borrow().has_popup_child {
212198
// We need to keep that element otherwise the popup will malfunction
213199
layout_child.borrow_mut().base_type = type_register.empty_type();
@@ -218,13 +204,13 @@ fn lower_grid_layout(
218204
} else {
219205
grid.add_element(
220206
&layout_child,
221-
(&mut row, &mut col),
207+
new_row,
222208
&layout_cache_prop_h,
223209
&layout_cache_prop_v,
224210
diag,
225211
);
226-
col += 1;
227212
collected_children.push(layout_child);
213+
new_row = false;
228214
}
229215
}
230216
grid_layout_element.borrow_mut().children = collected_children;
@@ -275,7 +261,7 @@ impl GridLayout {
275261
fn add_element(
276262
&mut self,
277263
item_element: &ElementRc,
278-
(row, col): (&mut u16, &mut u16),
264+
new_row: bool,
279265
layout_cache_prop_h: &NamedReference,
280266
layout_cache_prop_v: &NamedReference,
281267
diag: &mut BuildDiagnostics,
@@ -289,27 +275,34 @@ impl GridLayout {
289275
};
290276
let colspan = get_const_value("colspan").unwrap_or(1);
291277
let rowspan = get_const_value("rowspan").unwrap_or(1);
292-
if let Some(r) = get_const_value("row") {
293-
*row = r;
294-
*col = 0;
295-
}
296-
if let Some(c) = get_const_value("col") {
297-
*col = c;
298-
}
299278

300-
let result = self.add_element_with_coord(
279+
// TODO: use get_expr for colspan/rowspan too
280+
let mut get_expr = |name: &str| {
281+
item_element.borrow_mut().bindings.get(name).map(|e| {
282+
let expr = &e.borrow().expression;
283+
check_number_literal_is_positive_integer(expr, name, &*e.borrow(), diag);
284+
expr.clone()
285+
})
286+
};
287+
288+
let row_expr = get_expr("row");
289+
let col_expr = get_expr("col");
290+
291+
/*let result =*/
292+
self.add_element_with_coord_as_expr(
301293
item_element,
302-
(*row, *col),
294+
new_row,
295+
(&row_expr, &col_expr),
303296
(rowspan, colspan),
304297
layout_cache_prop_h,
305298
layout_cache_prop_v,
306299
diag,
307300
);
308-
if let Some(layout_item) = result {
301+
/*if let Some(layout_item) = result {
309302
let e = &layout_item.elem;
310-
insert_fake_property(e, "row", Expression::NumberLiteral(*row as f64, Unit::None));
311-
insert_fake_property(e, "col", Expression::NumberLiteral(*col as f64, Unit::None));
312-
}
303+
insert_fake_property(e, "row", row_expr);
304+
insert_fake_property(e, "col", col_expr);
305+
}*/
313306
}
314307

315308
fn add_element_with_coord(
@@ -320,6 +313,30 @@ impl GridLayout {
320313
layout_cache_prop_h: &NamedReference,
321314
layout_cache_prop_v: &NamedReference,
322315
diag: &mut BuildDiagnostics,
316+
) -> Option<CreateLayoutItemResult> {
317+
self.add_element_with_coord_as_expr(
318+
item_element,
319+
false, // new_row
320+
(
321+
&Some(Expression::NumberLiteral(row as _, Unit::None)),
322+
&Some(Expression::NumberLiteral(col as _, Unit::None)),
323+
),
324+
(rowspan, colspan),
325+
layout_cache_prop_h,
326+
layout_cache_prop_v,
327+
diag,
328+
)
329+
}
330+
331+
fn add_element_with_coord_as_expr(
332+
&mut self,
333+
item_element: &ElementRc,
334+
new_row: bool,
335+
(row_expr, col_expr): (&Option<Expression>, &Option<Expression>),
336+
(rowspan, colspan): (u16, u16),
337+
layout_cache_prop_h: &NamedReference,
338+
layout_cache_prop_v: &NamedReference,
339+
diag: &mut BuildDiagnostics,
323340
) -> Option<CreateLayoutItemResult> {
324341
let index = self.elems.len();
325342
let result = create_layout_item(item_element, diag);
@@ -344,8 +361,9 @@ impl GridLayout {
344361
}
345362

346363
self.elems.push(GridLayoutElement {
347-
col,
348-
row,
364+
new_row,
365+
col_expr: col_expr.clone(),
366+
row_expr: row_expr.clone(),
349367
colspan,
350368
rowspan,
351369
item: layout_item.item.clone(),
@@ -845,6 +863,32 @@ fn eval_const_expr(
845863
}
846864
}
847865

866+
// If it's a number literal, it must be a positive integer
867+
// But also allow any other kind of expression
868+
fn check_number_literal_is_positive_integer(
869+
expression: &Expression,
870+
name: &str,
871+
span: &dyn crate::diagnostics::Spanned,
872+
diag: &mut BuildDiagnostics,
873+
) {
874+
match super::ignore_debug_hooks(expression) {
875+
Expression::NumberLiteral(v, Unit::None) => {
876+
if *v > u16::MAX as f64 || !v.trunc().approx_eq(v) {
877+
diag.push_error(format!("'{name}' must be a positive integer"), span);
878+
}
879+
}
880+
Expression::UnaryOp { op: '-', sub } => {
881+
if let Expression::NumberLiteral(_, Unit::None) = super::ignore_debug_hooks(sub) {
882+
diag.push_error(format!("'{name}' must be a positive integer"), span);
883+
}
884+
}
885+
Expression::Cast { from, .. } => {
886+
check_number_literal_is_positive_integer(from, name, span, diag)
887+
}
888+
_ => {}
889+
}
890+
}
891+
848892
/// Checks that there is grid-layout specific properties left
849893
fn check_no_layout_properties(
850894
item: &ElementRc,

0 commit comments

Comments
 (0)