waterui-layout 0.2.1

Layout components for WaterUI
Documentation
# waterui-layout

Layout building blocks for arranging views in WaterUI applications.

## Overview

`waterui-layout` provides the fundamental layout primitives used to compose user interfaces in WaterUI. Unlike traditional UI frameworks that manually calculate positions, this crate implements a declarative, constraint-based layout system inspired by SwiftUI's layout protocol. All components render to native platform widgets (UIKit/AppKit on Apple, Android View on Android) rather than drawing custom pixels.

The crate bridges the declarative `View` trait with the imperative, backend-driven layout pass through the `Layout` trait, enabling flexible composition of stacks, spacers, frames, and scrollable containers. All layout values use logical pixels (points/dp) matching design tool specifications exactly, with native backends handling density-aware conversion to physical pixels.

## Installation

Add this to your `Cargo.toml`:

```toml
[dependencies]
waterui-layout = "0.1.0"
```

Or use the main `waterui` crate which re-exports all layout components:

```toml
[dependencies]
waterui = "0.2"
```

## Quick Start

Here's a simple toolbar layout demonstrating horizontal stacking and spacers:

```rust
use waterui_layout::{stack, spacer};
use waterui_text::text;
use waterui_core::View;

pub fn toolbar() -> impl View {
    stack::hstack((
        text("WaterUI"),
        spacer(),
        text("v0.1"),
    ))
    .spacing(8.0)
}
```

This creates a horizontal layout with "WaterUI" on the left, "v0.1" on the right, and flexible space between them.

## Core Concepts

### Layout Trait

The `Layout` trait defines how containers arrange their children through a two-phase protocol:

1. **Sizing Phase**: `size_that_fits(proposal, children)` determines the container's size given a parent proposal
2. **Placement Phase**: `place(bounds, children)` positions children within the final bounds

Layouts can query children multiple times with different proposals to negotiate optimal sizing.

### Stretch Behavior

Views communicate their flexibility through `StretchAxis`:

- `None` - Content-sized, uses intrinsic dimensions
- `Horizontal` - Expands width only (e.g., TextField)
- `Vertical` - Expands height only
- `Both` - Greedy, fills all space (e.g., Spacer, Color)
- `MainAxis` - Stretches along parent's main axis (used by Spacer)
- `CrossAxis` - Stretches along parent's cross axis (used by Divider)

Stack containers distribute remaining space among stretching children proportionally.

### Logical Pixels

All layout values use **logical pixels** (points/dp) - the same unit as Figma, Sketch, and Adobe XD:

- `spacing(8.0)` = 8pt in design tools
- `width(100.0)` = 100pt/dp, same physical size across all devices
- iOS/macOS: Uses points natively
- Android: Converts dp → pixels via `displayMetrics.density`

This ensures pixel-perfect design implementation across platforms.

## Examples

### Building a Form Layout

```rust
use waterui_layout::{stack, padding::EdgeInsets};
use waterui_text::text;
use waterui_controls::TextField;
use waterui_reactive::binding;
use waterui_core::View;

pub fn login_form() -> impl View {
    let username = binding("");
    let password = binding("");

    stack::vstack((
        text("Login").size(24.0).bold(),
        TextField::new(&username)
            .label(text("Username"))
            .prompt("Enter username"),
        TextField::new(&password)
            .label(text("Password"))
            .prompt("Enter password")
            .secure(true),
    ))
    .alignment(stack::HorizontalAlignment::Leading)
    .spacing(16.0)
    .padding_with(EdgeInsets::all(20.0))
}
```

### Creating a Toolbar with Spacers

```rust
use waterui_layout::{stack, spacer};
use waterui_controls::button;
use waterui_text::text;
use waterui_core::View;

pub fn app_toolbar() -> impl View {
    stack::hstack((
        button("Menu", || { /* action */ }),
        spacer(),
        text("My App").bold(),
        spacer(),
        button("Settings", || { /* action */ }),
    ))
    .spacing(12.0)
}
```

### Overlaying Content

```rust
use waterui_layout::{stack, overlay, stack::Alignment};
use waterui_graphics::Color;
use waterui_text::text;
use waterui_core::View;

pub fn badge_overlay() -> impl View {
    overlay(
        Color::blue().frame(100.0, 100.0),
        text("5").foreground(Color::white()),
    )
    .alignment(Alignment::TopTrailing)
}
```

### Scrollable Content

```rust
use waterui_layout::{scroll, stack};
use waterui_text::text;
use waterui_core::View;

pub fn scrollable_list() -> impl View {
    scroll(
        stack::vstack((
            text("Item 1"),
            text("Item 2"),
            text("Item 3"),
            // ... many more items
        ))
        .spacing(10.0)
    )
}
```

### Responsive Layout with Frames

```rust
use waterui_layout::frame::Frame;
use waterui_layout::stack::Alignment;
use waterui_text::text;
use waterui_core::View;

pub fn constrained_content() -> impl View {
    Frame::new(text("Limited Width"))
        .min_width(100.0)
        .max_width(300.0)
        .height(50.0)
        .alignment(Alignment::Center)
}
```

## API Overview

### Stack Containers

- **`stack::hstack(content)`** - Arranges children horizontally left-to-right

  - `.spacing(f32)` - Sets spacing between children
  - `.alignment(VerticalAlignment)` - Sets vertical alignment (Top, Center, Bottom)

- **`stack::vstack(content)`** - Arranges children vertically top-to-bottom

  - `.spacing(f32)` - Sets spacing between children
  - `.alignment(HorizontalAlignment)` - Sets horizontal alignment (Leading, Center, Trailing)

- **`stack::zstack(content)`** - Overlays children in the same space
  - `.alignment(Alignment)` - Sets 2D alignment for overlaid content

### Layout Primitives

- **`spacer()`** - Flexible space that expands to push views apart
- **`spacer_min(f32)`** - Spacer with minimum length
- **`ScrollView`** - Scrollable container for overflow content

  - `scroll(content)` - Vertical scrolling
  - `scroll_horizontal(content)` - Horizontal scrolling
  - `scroll_both(content)` - Bidirectional scrolling

- **`Frame`** - Constrains child size with min/max/ideal dimensions

  - `.width(f32)`, `.height(f32)` - Sets ideal dimensions
  - `.min_width(f32)`, `.max_width(f32)` - Sets size constraints
  - `.alignment(Alignment)` - Aligns child within frame

- **`Padding`** - Insets child with edge spacing
  - `EdgeInsets::all(f32)` - Equal padding on all edges
  - `EdgeInsets::symmetric(vertical, horizontal)` - Symmetric padding
  - `EdgeInsets::new(top, bottom, leading, trailing)` - Custom edges

### Advanced Layouts

- **`overlay(base, layer)`** - Layers content on top of base without affecting layout size
- **`OverlayLayout`** - Layout engine where base child dictates container size
- **`LazyContainer`** - Efficient container for dynamic collections with `ForEach`
- **`IgnoreSafeArea`** - Metadata to extend content into safe area regions
  - `EdgeSet` - Bitflags for specifying which edges ignore safe area

### Alignment Types

- **`Alignment`** - 2D alignment (TopLeading, Top, TopTrailing, Leading, Center, Trailing, BottomLeading, Bottom, BottomTrailing)
- **`HorizontalAlignment`** - Leading, Center, Trailing
- **`VerticalAlignment`** - Top, Center, Bottom
- **`Axis`** - Horizontal, Vertical (for stack direction)

## Features

This crate supports optional features:

- **`serde`** - Enables serialization/deserialization of layout types via serde

Enable features in your `Cargo.toml`:

```toml
[dependencies]
waterui-layout = { version = "0.1.0", features = ["serde"] }
```

## Architecture Notes

### Backend Integration

Layouts communicate with native backends through the `Layout` trait's protocol:

1. Backend calls `size_that_fits(proposal, children)` to measure
2. Backend calls `place(bounds, children)` to get child rectangles
3. Backend renders native widgets at the calculated positions

The FFI layer in `waterui-ffi` handles the Rust ↔ Native boundary.

### Performance Characteristics

- All layout calculations happen in Rust, then native backends cache results
- The `SubView` trait enables measurement caching at the platform level
- Lazy containers (`LazyContainer`) defer child instantiation for large collections
- Layout is pure (no side effects), enabling aggressive optimization by backends

### Layout Compression

When children exceed available space:

- **HStack**: Compresses largest non-stretching children first, preserving small labels
- **VStack**: Children maintain intrinsic heights, may overflow bounds (scrollable)
- Minimum size enforcement prevents unreadable content (20pt minimum for compressed children)

This behavior matches native platform conventions for graceful degradation.