Context
Compose's Layout {} composable is the low-level primitive that lets caller code measure its children directly and decide where to place them. Stock containers (Column / Row / Box / LazyColumn) cover 90% of cases, but the remaining 10% — flow layouts with custom break rules, adaptive multi-column lists that re-balance at runtime, anchored badge overlays — needs Layout.
JetNews's upstream InterestsAdaptiveContentLayout is exactly this pattern: render N topics in M columns where M depends on available width. Our port falls back to a single column. Jetsnack and Jetcaster have similar bespoke layouts.
Proposal
Bind androidx.compose.ui.layout.LayoutKt.Layout(content, modifier, measurePolicy, composer, _changed) and expose a C# Layout facade that takes:
content: ComposableNode (children)
measurePolicy: Func<MeasureScope, List<Measurable>, Constraints, MeasureResult>
The hard part is binding Measurable / Placeable / MeasureResult / MeasureScope and the placement DSL (layout(width, height) { children.forEach { placeRelative(...) } }). Likely needs:
Measurable.Measure(Constraints) → Placeable
Placeable.PlaceRelative(int x, int y) / Place(int x, int y)
MeasureScope.Layout(int width, int height, Action<Placeable.PlacementScope> placementBlock) → MeasureResult
Subscope receivers (PlacementScope) probably need the same IntPtr scope parameter trick the existing RowScope / ColumnScope facade families use.
Use case
Three immediate use cases:
- JetNews's
InterestsAdaptiveContentLayout (multi-column topic grid that re-balances on rotation).
- Jetsnack's snack carousels with offset overlays.
- Asymmetric chat bubbles in Jetchat (a tail triangle anchored to one corner of the bubble).
Closing this would also let IntrinsicSize.Max work — that one needs a custom MeasurePolicy to evaluate.
References
Context
Compose's
Layout {}composable is the low-level primitive that lets caller code measure its children directly and decide where to place them. Stock containers (Column/Row/Box/LazyColumn) cover 90% of cases, but the remaining 10% — flow layouts with custom break rules, adaptive multi-column lists that re-balance at runtime, anchored badge overlays — needsLayout.JetNews's upstream
InterestsAdaptiveContentLayoutis exactly this pattern: render N topics in M columns where M depends on available width. Our port falls back to a single column. Jetsnack and Jetcaster have similar bespoke layouts.Proposal
Bind
androidx.compose.ui.layout.LayoutKt.Layout(content, modifier, measurePolicy, composer, _changed)and expose a C#Layoutfacade that takes:content: ComposableNode(children)measurePolicy: Func<MeasureScope, List<Measurable>, Constraints, MeasureResult>The hard part is binding
Measurable/Placeable/MeasureResult/MeasureScopeand the placement DSL (layout(width, height) { children.forEach { placeRelative(...) } }). Likely needs:Measurable.Measure(Constraints) → PlaceablePlaceable.PlaceRelative(int x, int y)/Place(int x, int y)MeasureScope.Layout(int width, int height, Action<Placeable.PlacementScope> placementBlock) → MeasureResultSubscope receivers (
PlacementScope) probably need the sameIntPtr scopeparameter trick the existingRowScope/ColumnScopefacade families use.Use case
Three immediate use cases:
InterestsAdaptiveContentLayout(multi-column topic grid that re-balances on rotation).Closing this would also let
IntrinsicSize.Maxwork — that one needs a customMeasurePolicyto evaluate.References
Canvasshares some of the same DSL plumbing)