Skip to content

Bind custom Layout {} primitive — Measurable / Placeable / MeasureScope #144

@jonathanpeppers

Description

@jonathanpeppers

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:

  1. JetNews's InterestsAdaptiveContentLayout (multi-column topic grid that re-balances on rotation).
  2. Jetsnack's snack carousels with offset overlays.
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions