-
Notifications
You must be signed in to change notification settings - Fork 780
Lay out grids at runtime rather than at compile time #9572
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
ogoffart
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is going in the right direction.
We'll have to have good documentation for the exact behavior and tests.
Phew ;-)
Documentation, I agree (will do). |
63da9fb to
04f0bdc
Compare
|
Now including row and col properties, implemented using the layout cache. The other issue is the one mentioned in the commit log: how to react to changes to row/col and trigger layouting again? |
ogoffart
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works, but it has an issue: if e.g. a widget's text property is set to "row" or "col", it creates an infinite recursion on the layout-cache binding, incorrectly.
I see, because the row and col are both input and output of the layout computation?
It makes sense to x,y,width,height to depend on the widget text, but not for row and col. So I should create a different kind of cache for that?
Perhaps it would help to have another property to cache these values instead of doing it all in solve_layout, yes.
(Then we would also not need this cache if the row and col are entirely known at compile time)
But this can be a known issue and doesn't need to be solve, as long as it is detected at compile time and the logic in the binding_analysis that detect the loop is catching that properly.
Hmm, solve_grid_layout returns the x,y,width,height cache (for use in the property binding for layoutcache) and takes the spec from the user as input, so row/col determination has to happen in there too (it's obviously needed to determine x,y,width,height)... but the result of that row/col determination needs to be stored into another property... I wonder how. The current solution is already to duplicate the input data between layoutcache (the call to solve_grid_layout) and layoutinfo (the call to grid_layout_info). Somehow we should be able to extract that into a function that returns a Vec, that I could also use from the third property that is computed from that same data?
That is an excellent idea, it would address your concern about memory usage.
Yes, it's caught properly, but it's still a bug, and one that requires a big refactoring of the code in this MR. But indeed if we want the feature already (or if you simply want to avoid re-reviewing the same thing many times), you can merge this and I can build on top. |
04f0bdc to
d5fcbb7
Compare
I was wrong, this works well in an interactive testcase. It just doesn't work in a unittest like |
Maybe something like |
d5fcbb7 to
04ea92e
Compare
Thanks, this worked, now the new test passes (both when compiled and when interpreted) |
04ea92e to
d60dddd
Compare
I quite like this idea, actually. I am currently implementing this solution, on top of this MR, to make this cleaner and solve the binding loop. A new property "layout-organized-data" contains the result of the call that uses the input data to determine row/col values and store that into the organized data (which is a SharedVector in order to reuse the LayoutCacheAccess mechanism, just on a different cache property), and this (plus layout constraints...) will be the source of information for both solve_grid_layout and grid_layout_info. Currently only implemented for the interpreter (works 100% - grid_layout_data() reads from the organized data and adds the constraints, it all works including a text property that uses row and col), I will now work on the compiler side of things. |
70e6c49 to
8e8ee7b
Compare
This is implemented now. I quite like the result. This cache of row/col/rowspan/colspan values is only recomputed when needed (fixing the binding loop mentioned previously), and is used for everything: for the user-accessible properties, and for the two layouting algorithms (solve_grid_layout and grid_layout_info). And the logic for reordering dialog buttons is no longer a hack applied into the data structures used for layouting, it's now the implementation of "organize" for the case of dialogs (which don't need the default "organize" step, the one which deals with auto numbering and with runtime expressions for row/col). The whole thing can be quite a bit confusing, so I made this diagram: |
81f31d6 to
bcb4d60
Compare
ogoffart
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks.
Just a note about the fact that we added expression tothe grid layout structure and we don't visit them.
I think this might cause the compiler to not see them when trying to see if everything is used or detecting binding loops.
Another test could be a test with a binding loop in
col: self.x > 10px ? 3 : 4; or something like that (it should correctly be detected as a binding loop.)
| col: 1; | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could you also have an extra test that does foo.col = xyz in a callback and that mix that with auto-numbering?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, so the error about mixing auto with runtime-expr needs to be a runtime error too, not just a compile time error? Or do you think we can detect such an issue at compile time already? For compile-time I'd need to iterate over all cells any time the compiler is seeing a statement like foo.col = xyz (after detecting this special case of assigning to a row or col property). Do we have anything like this already, for inspiration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say you can use the PropertyAnalysis::is_set for that property to know that this property is set by some binding. and therefore disallow mixing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I didn't know about PropertyAnalysis. Unfortunately it runs after lower_layout so it's not available there...
One thing about this testcase:
- if
foodoesn't definecol, then nothing happens when settingfoo.colbecause the generated code treated the value of col as a constant, so it generatedthe_struct.r#col = 65535f64 as _;, not reading a property. - if
foohascol: 1;orcol: <runtime_expr>then the generated code reads from thefoo_colproperty, so changing it works (but we only want that if all row and col properties in the layout are set, otherwise we're back to the what-happens-to-neighbors issues).
OK, so actually, all we need is to store into the GridLayout in which mode it is: only-auto-and-literal or explicit-row-col-everywhere (in other words: uses auto yes/no). Then during property analysis we can warn if changing a row or col of a grid layout that uses auto anywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But PropertyAnalysis::is_set won't allow to point the error to the location where it's set...
Apart from that, it works.
This includes support for row and col properties to be expressions rather than just constants.
ChangeLog: the row, col, rowspan and colspan properties can now be expressions rather than having to be constants. The layout adapts when the value of the expression changes.
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.
This repairs the feature of reordering buttons, which was temporarily left out in the previous commit.
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...
eacc332 to
b6d7010
Compare
911efef to
c004a20
Compare
…/colspan With testcase from Olivier for a binding loop col -> x -> col
348682e to
d030fad
Compare
|
What's the reason for the CI failure? I don't see any error message. |
|
The compiler crashing I think:(. Seems to trigger often with rust 1.88. cleared the cache, re-run, now it's green :) |
This opens the door for using computed values in "row"/"col" properties, and later on for dynamic grid layouts (for/if support)
PROBLEM: it's missing the "row" and "col" properties from my previous commit... This is WIP, so we can discuss how to bring those properties back.