Skip to content

Commit 8983907

Browse files
dfaure-kdabogoffart
authored andcommitted
compiler: forbid mixing auto and runtime-expr for GridLayout row/col
Let's call "auto" the case where the slint file doesn't specify the row or col for a cell in a grid layout. When the runtime expression for row/col changes, what should happen to the subsequent cells with 'auto' behavior for their column number (including the case where a change of row implies restarting at column 0) ? Option A: they move with it, as if it had all been specified that way at compile time (dynamic auto behavior) Option B: they stay where they are (compile-time-only auto behavior) Both have upsides and downsides (the risk of colliding with an existing cell exists either way), so the current decision is: let's just forbid it. Note that the code currently implements option A, so if one day it's decided in favor of that, just revert this commit...
1 parent 804ca74 commit 8983907

File tree

3 files changed

+92
-15
lines changed

3 files changed

+92
-15
lines changed

internal/compiler/passes/lower_layout.rs

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,26 @@ fn lower_element_layout(
151151
layout_type
152152
}
153153

154+
// to detect mixing auto and non-literal expressions in row/col values
155+
#[derive(Debug, PartialEq, Eq)]
156+
enum RowColExpressionType {
157+
Auto, // not specified
158+
Literal,
159+
RuntimeExpression,
160+
}
161+
impl RowColExpressionType {
162+
fn from_option_expr(
163+
expr: &Option<Expression>,
164+
is_number_literal: bool,
165+
) -> RowColExpressionType {
166+
match expr {
167+
None => RowColExpressionType::Auto,
168+
Some(_) if is_number_literal => RowColExpressionType::Literal,
169+
Some(_) => RowColExpressionType::RuntimeExpression,
170+
}
171+
}
172+
}
173+
154174
fn lower_grid_layout(
155175
component: &Rc<Component>,
156176
grid_layout_element: &ElementRc,
@@ -192,6 +212,7 @@ fn lower_grid_layout(
192212
let layout_children = std::mem::take(&mut grid_layout_element.borrow_mut().children);
193213
let mut collected_children = Vec::new();
194214
let mut new_row = false; // true until the first child of a Row, or the first item after an empty Row
215+
let mut numbering_type: Option<RowColExpressionType> = None;
195216
for layout_child in layout_children {
196217
let is_row = if let ElementType::Builtin(be) = &layout_child.borrow().base_type {
197218
be.name == "Row"
@@ -214,6 +235,7 @@ fn lower_grid_layout(
214235
&layout_cache_prop_h,
215236
&layout_cache_prop_v,
216237
&layout_organized_data_prop,
238+
&mut numbering_type,
217239
diag,
218240
);
219241
collected_children.push(x);
@@ -234,6 +256,7 @@ fn lower_grid_layout(
234256
&layout_cache_prop_h,
235257
&layout_cache_prop_v,
236258
&layout_organized_data_prop,
259+
&mut numbering_type,
237260
diag,
238261
);
239262
collected_children.push(layout_child);
@@ -314,20 +337,24 @@ impl GridLayout {
314337
layout_cache_prop_h: &NamedReference,
315338
layout_cache_prop_v: &NamedReference,
316339
organized_data_prop: &NamedReference,
340+
numbering_type: &mut Option<RowColExpressionType>,
317341
diag: &mut BuildDiagnostics,
318342
) {
319343
let mut get_expr = |name: &str| {
320-
item_element.borrow_mut().bindings.get(name).map(|e| {
344+
let mut is_number_literal = false;
345+
let expr = item_element.borrow_mut().bindings.get(name).map(|e| {
321346
let expr = &e.borrow().expression;
322-
check_number_literal_is_positive_integer(expr, name, &*e.borrow(), diag);
347+
is_number_literal =
348+
check_number_literal_is_positive_integer(expr, name, &*e.borrow(), diag);
323349
expr.clone()
324-
})
350+
});
351+
(expr, is_number_literal)
325352
};
326353

327-
let row_expr = get_expr("row");
328-
let col_expr = get_expr("col");
329-
let rowspan_expr = get_expr("rowspan");
330-
let colspan_expr = get_expr("colspan");
354+
let (row_expr, row_is_number_literal) = get_expr("row");
355+
let (col_expr, col_is_number_literal) = get_expr("col");
356+
let (rowspan_expr, _) = get_expr("rowspan");
357+
let (colspan_expr, _) = get_expr("colspan");
331358

332359
self.add_element_with_coord_as_expr(
333360
item_element,
@@ -339,6 +366,32 @@ impl GridLayout {
339366
organized_data_prop,
340367
diag,
341368
);
369+
370+
let mut check_numbering_consistency = |expr_type: RowColExpressionType, prop_name: &str| {
371+
if !matches!(expr_type, RowColExpressionType::Literal) {
372+
if let Some(current_numbering_type) = numbering_type {
373+
if *current_numbering_type != expr_type {
374+
item_element.borrow_mut().bindings.get(prop_name).map(|binding| {
375+
diag.push_error(
376+
format!("Cannot mix auto-numbering and runtime expressions for the '{prop_name}' property"),
377+
&*binding.borrow(),
378+
);
379+
});
380+
}
381+
} else {
382+
// Store the first auto or runtime expression case we see
383+
*numbering_type = Some(expr_type);
384+
}
385+
}
386+
};
387+
388+
let row_expr_type =
389+
RowColExpressionType::from_option_expr(&row_expr, row_is_number_literal);
390+
check_numbering_consistency(row_expr_type, "row");
391+
392+
let col_expr_type =
393+
RowColExpressionType::from_option_expr(&col_expr, col_is_number_literal);
394+
check_numbering_consistency(col_expr_type, "col");
342395
}
343396

344397
fn add_element_with_coord(
@@ -910,27 +963,30 @@ fn set_prop_from_cache(
910963

911964
// If it's a number literal, it must be a positive integer
912965
// But also allow any other kind of expression
966+
// Returns true for literals, false for other kinds of expressions
913967
fn check_number_literal_is_positive_integer(
914968
expression: &Expression,
915969
name: &str,
916970
span: &dyn crate::diagnostics::Spanned,
917971
diag: &mut BuildDiagnostics,
918-
) {
972+
) -> bool {
919973
match super::ignore_debug_hooks(expression) {
920974
Expression::NumberLiteral(v, Unit::None) => {
921975
if *v > u16::MAX as f64 || !v.trunc().approx_eq(v) {
922976
diag.push_error(format!("'{name}' must be a positive integer"), span);
923977
}
978+
true
924979
}
925980
Expression::UnaryOp { op: '-', sub } => {
926981
if let Expression::NumberLiteral(_, Unit::None) = super::ignore_debug_hooks(sub) {
927982
diag.push_error(format!("'{name}' must be a positive integer"), span);
928983
}
984+
true
929985
}
930986
Expression::Cast { from, .. } => {
931987
check_number_literal_is_positive_integer(from, name, span, diag)
932988
}
933-
_ => {}
989+
_ => false,
934990
}
935991
}
936992

internal/compiler/tests/syntax/basic/layout2.slint

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ export X := Rectangle {
66

77
lay := GridLayout {
88
property<string> foo: "hello";
9-
property<int> last_row: 4;
10-
property<int> negative: -5;
119
Row {
1210
Text {
1311
text: lay.foo + parent.width;
@@ -36,10 +34,7 @@ export X := Rectangle {
3634
}
3735
}
3836
}
39-
Text {
40-
row: lay.last_row;
41-
col: -lay.negative; // check that a unary minus isn't necessary an error
42-
}
37+
4338
Row {
4439
Text {
4540
x: 12px;
@@ -53,6 +48,30 @@ export X := Rectangle {
5348
}
5449
}
5550

51+
lay2 := GridLayout {
52+
// ^error{Cannot mix auto-numbering and runtime expressions for the 'col' property}
53+
property<int> negative: -5;
54+
property<int> last_row: 4;
55+
Text {
56+
row: lay2.last_row;
57+
col: -lay2.negative; // check that a unary minus isn't necessary an error
58+
}
59+
Text {
60+
row: 0; // mixing runtime expr and literal is ok
61+
}
62+
}
63+
64+
lay3 := GridLayout {
65+
// ^error{Cannot mix auto-numbering and runtime expressions for the 'row' property}
66+
Text {
67+
row: lay2.last_row;
68+
col: lay2.last_row;
69+
}
70+
Text {
71+
col: 1;
72+
}
73+
}
74+
5675
Text { colspan: 3; }
5776
// ^error{colspan used outside of a GridLayout's cell}
5877
col: 3;

tests/cases/layout/grid_variable_row_col.slint

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export component TestCase inherits Window {
3131
}
3232
txt := Text {
3333
text: txt.row + "," + txt.col;
34+
row: 0;
35+
col: green_col + 1;
3436
}
3537
rr := Rectangle {
3638
background: red;

0 commit comments

Comments
 (0)