# Layout

Plushie's layout model mirrors iced's. Understanding it is essential for
building UIs that size and position correctly.

## Length

Length controls how a widget claims space along an axis.

| Plushie value | Iced equivalent | Meaning |
|---|---|---|
| `:fill` | `Length::Fill` | Take all remaining space |
| `{:fill_portion, n}` | `Length::FillPortion(n)` | Proportional share of remaining space |
| `:shrink` | `Length::Shrink` | Use minimum/intrinsic size |
| `200` or `200.0` | `Length::Fixed(200.0)` | Exact pixel size |

<!-- test: layout_length_fill_test, layout_length_fixed_test, layout_length_fill_portion_test, layout_length_shrink_test -- keep this code block in sync with the test -->
```elixir
# Fill available width
column width: :fill do ... end

# Fixed width
container "sidebar", width: 250 do ... end

# Proportional: left takes 2/3, right takes 1/3
row do
  container "left", width: {:fill_portion, 2} do ... end
  container "right", width: {:fill_portion, 1} do ... end
end

# Shrink to content
button("save", "Save", width: :shrink)
```

### Default lengths

Most widgets default to `:shrink` for both width and height. Layout
containers (`column`, `row`) typically default to `:shrink` but grow to
accommodate their children.

## Padding

Padding is the space between a widget's boundary and its content.

| Plushie value | Meaning |
|---|---|
| `10` | Uniform: 10px on all sides |
| `{10, 20}` | Axis: 10px vertical, 20px horizontal |
| `%{top: 5, right: 10, bottom: 5, left: 10}` | Per-side |
| `0` | No padding |

<!-- test: layout_padding_uniform_test, layout_padding_xy_test, layout_padding_per_side_test -- keep this code block in sync with the test -->
```elixir
container "box", padding: 16 do ... end
container "box", padding: {8, 16} do ... end
container "box", padding: %{top: 0, right: 16, bottom: 8, left: 16} do ... end
```

Padding is accepted by `container`, `column`, `row`, `scrollable`,
`button`, `text_input`, and `text_editor`.

## Spacing

Spacing is the gap between children in a layout container.

<!-- test: layout_spacing_test -- keep this code block in sync with the test -->
```elixir
column spacing: 8 do
  text("First")
  text("Second")   # 8px gap between First and Second
  text("Third")    # 8px gap between Second and Third
end
```

Spacing is accepted by `column`, `row`, and `scrollable`.

## Alignment

Alignment controls how children are positioned within their parent along
the cross axis.

### align_x (horizontal alignment in a column)

| Value | Meaning |
|---|---|
| `:start` or `:left` | Left-aligned |
| `:center` | Centered |
| `:end` or `:right` | Right-aligned |

### align_y (vertical alignment in a row)

| Value | Meaning |
|---|---|
| `:start` or `:top` | Top-aligned |
| `:center` | Centered |
| `:end` or `:bottom` | Bottom-aligned |

<!-- test: layout_align_x_column_test, layout_align_center_container_test -- keep this code block in sync with the test -->
```elixir
# Center children horizontally in a column
column align_x: :center do
  text("Centered")
  button("ok", "OK")
end

# Center a single child in a container
container "page", width: :fill, height: :fill, center: true do
  text("Dead center")
end
```

The `center: true` shorthand on `container` sets both `align_x: :center`
and `align_y: :center`.

## Layout containers

### column

Arranges children vertically (top to bottom).

<!-- test: layout_column_with_props_test -- keep this code block in sync with the test -->
```elixir
column id: "main", spacing: 16, padding: 20, width: :fill, align_x: :center do
  text("title", "Title", size: 24)
  text("subtitle", "Subtitle", size: 14)
end
```

Props: `spacing`, `padding`, `width`, `height`, `align_x`.

### row

Arranges children horizontally (left to right).

<!-- test: layout_row_with_align_y_test -- keep this code block in sync with the test -->
```elixir
row spacing: 8, align_y: :center do
  button("back", "<")
  text("Page 1 of 5")
  button("next", ">")
end
```

Props: `spacing`, `padding`, `width`, `height`, `align_y`, `wrap` (new
in plushie-iced -- wraps children to next line when they overflow).

### container

Wraps a single child with padding, alignment, and styling.

<!-- test: layout_container_with_style_test -- keep this code block in sync with the test -->
```elixir
container "card", padding: 16, style: :rounded_box, width: :fill do
  column do
    text("Card title")
    text("Card content")
  end
end
```

Props: `padding`, `width`, `height`, `align_x`, `align_y`, `center`,
`style`, `clip`.

### scrollable

Wraps content in a scrollable region.

<!-- test: layout_scrollable_test -- keep this code block in sync with the test -->
```elixir
scrollable "list", height: 400, width: :fill do
  column spacing: 4 do
    for item <- items do
      text("item:#{item.id}", item.name)
    end
  end
end
```

Props: `width`, `height`, `direction` (`:vertical`, `:horizontal`,
`:both`), `spacing`.

### stack

Overlays children on top of each other (z-stacking). Later children
are on top.

<!-- test: layout_stack_test -- keep this code block in sync with the test -->
```elixir
stack do
  image("bg", "background.png", width: :fill, height: :fill)
  container "overlay", width: :fill, height: :fill, center: true do
    text("overlay_text", "Overlaid text", size: 48)
  end
end
```

### space

Empty spacer. Takes up space without rendering anything.

<!-- test: layout_space_test -- keep this code block in sync with the test -->
```elixir
row do
  text("Left")
  space(width: :fill)  # pushes Right to the far right
  text("Right")
end
```

### grid

Arranges children in a grid layout (new in plushie-iced).

<!-- test: layout_grid_test -- keep this code block in sync with the test -->
```elixir
grid id: "gallery", columns: 3, spacing: 8 do
  for item <- items do
    image("img:#{item.id}", item.url, width: :fill)
  end
end
```

## Common layout patterns

### Centered page

<!-- test: layout_centered_page_test -- keep this code block in sync with the test -->
```elixir
container "page", width: :fill, height: :fill, center: true do
  column spacing: 16, align_x: :center do
    text("welcome", "Welcome", size: 32)
    button("start", "Get Started")
  end
end
```

### Sidebar + content

```elixir
row width: :fill, height: :fill do
  container "sidebar", width: 250, height: :fill, padding: 16 do
    nav_items(model)
  end
  container "content", width: :fill, height: :fill, padding: 16 do
    main_content(model)
  end
end
```

### Header + body + footer

```elixir
column width: :fill, height: :fill do
  container "header", width: :fill, padding: {8, 16} do
    header(model)
  end
  scrollable "body", width: :fill, height: :fill do
    body_content(model)
  end
  container "footer", width: :fill, padding: {8, 16} do
    footer(model)
  end
end
```
