Clarification of behaviour and expectations of the layout API
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 9.7k
- Forks
- 565
- PR merge metrics
- No merged PRs in 30d
Description
*what does it mean for a widget to be given a size different from the one it returned from its `layout` method?*
## the problem
In doing the baseline work, I have been exploring making Widget::layout return a more general type than `Size`. Over time, we have added additional metrics that are computed in layout. The first of these was the 'paint insets', which describe the area outside of the view's layout size where the view may draw; and now I've been adding baseline positions, which are offsets relative to the edge of the layout's size that are used to align horizontal text.
Currently, these additional metrics are added through calls on the `LayoutCtx`; for instance, `LayoutCtx::set_paint_insets`. The idea was to hopefully simplify this, by having all of these metrics be returned directly from the layout call, when applicable.
Of course, there's a problem.
This problem isn't specific to the redesign; it relates to the fact that the *actual* size of a widget is not the necessarily the size returned from the widget's `layout` method; it is a size chosen by whichever ancestor is managing the widget's `WidgetPod`. *In the general case* this should be the size returned by the widget, but this is not enforced; the parent can in theory chose an arbitrary size.
What does it mean for the parent to choose a size different from that returned by the child? This is not really written down anywhere; nor is there a clear guideline for how the child is expected to adapt when drawing to an unexpected size.
This potential conflict poses problems for things like baselines and paint insets. These metrics are relative to the size calculated in layout, and will be invalid if the widget ends up being provided with a different size.
## tentative proposal
I think we can improve on this situation, by clarifying our invariants and how we will behave when they are not met. I propose the following:
- If a widget returns a valid size from `layout` (defined as a size that satisfies the provided constraints) the widget will *always* be given the same size in `paint`.
- If the parent widget *needs* to paint the child in a size other than what the child requested, the child's `paint` method will be called with the child's expected size, and the parent will chose to either clip or overflow the content.
- All metrics (such as baselines and paint insets) will be relative to the 'layout size', that is the size returned from `Widget::layout`.
- `WidgetPod::set_layout_rect` may be replaced with `WidgetPod::set_layout_pos`?
The main advantage of this is that it significantly simplifies layout; The `WidgetPod` itself can stash the result of its child's `layout` method, and know that the provided values are valid.
## questions
I'm not sure about this last bit; in particular I'm not sure if we should maintain a distinction between the 'layout rect' returned by the child and the 'paint rect' determined by the parent. Having these be separate, and sharing an origin, would allow us to correctly adjust paint insets and baselines as appropriate, but would increase complexity somewhat.
Ultimately, this comes down to a fairly straight-forward question: what does it mean for a parent to provide a different size than that returned by the child? Is this an okay thing during normal execution, or does this violate some contract? If it violates some contract, and potentially interferes in other layout, I think that's okay; we can log some warnings and expect the user to fix their code. If however there is some **good reason** why this behaviour may be required, we should be more defensive.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the Widget::layout and paint contracts, then trace how WidgetPod handles child sizes and WidgetPod::set_layout_rect. Review LayoutCtx::set_paint_insets and the proposed baseline metrics to identify which invariants existing widgets rely on. Done means documenting and agreeing on the parent-child size contract and its effects on paint insets and baselines.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- frontend
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100