# Plano de Implementação — V0.4 (Rich Content)

**Spec de referência:** `2026-05-17-post-v1-roadmap-design.md`  
**Pré-requisito:** V0.3 completo (Scroll, TextInput, clipping implementados)  
**Critério de conclusão:** `cargo test --workspace` + `cargo clippy --workspace --all-targets -- -D warnings` passam. Exemplo com imagem, select e slider funcionais.

---

## Ordem de execução

`ImageHandle + Image paint` → `Z-index` → `Select` → `Slider` → `Scrollbar visual`  
Z-index é pré-requisito do Select (popup). Slider é independente. Scrollbar visual depende de z-index.

---

## Etapa 1 — `ImageHandle` e Assets

### 1.1 Tipo `ImageHandle`

Arquivo: `src/src/asset/image.rs` (módulo novo `asset`)

```rust
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct ImageHandle(Arc<ImageData>);

struct ImageData {
    width: u32,
    height: u32,
    pixels: Vec<u8>,  // RGBA8
}

impl ImageHandle {
    pub fn from_bytes(bytes: &[u8]) -> Result<Self, ImageError> { ... }
    pub fn from_path(path: impl AsRef<Path>) -> Result<Self, ImageError> { ... }
}
```

- `from_bytes`: decodificar via `image` crate (feature `png`, `jpeg`)
- `from_path`: ler arquivo e delegar a `from_bytes`
- `PartialEq` + `Hash` por ponteiro `Arc` (identidade, não conteúdo)

### 1.2 Cache de texturas

Arquivo: `src/src/asset/cache.rs`

- `TextureCache: HashMap<ImageHandle, wgpu::Texture>`
- Vive no `AppState` (platform layer)
- `get_or_upload(handle, device, queue) -> &wgpu::Texture`
- Upload: criar `wgpu::Texture` RGBA8, `write_texture` com os pixels

### 1.3 Adicionar ao workspace

- `image = { version = "0.25", default-features = false, features = ["png", "jpeg"] }` em `[workspace.dependencies]`
- Dependência em `src/Cargo.toml`

---

## Etapa 2 — Image paint

### 2.1 `Element::Prim` com conteúdo de imagem

O elemento `Image` em `lemon::widget` retorna um `Prim` com um novo campo de paint:

```rust
pub enum PaintContent {
    None,
    Color(ColorSource),
    Image(ImageHandle),
}
```

- Adicionar `content: PaintContent` em `PaintProps` (substitui ou complementa `background`)

### 2.2 `PaintData` no retained tree

- `PaintData` armazena `PaintContent` após diff
- `Patch::UpdatePaint` propagado normalmente

### 2.3 Paint pass

Arquivo: `src/src/paint/mod.rs`

- Quando `PaintContent::Image(handle)`:
  - Buscar textura em `TextureCache`
  - Usar `vello::peniko::Image` + `scene.draw_image` para renderizar no rect do nó
  - Preservar aspect ratio: `object-fit: contain` por padrão (calculado em layout ou paint)

### 2.4 Builder `Image` em lemon::widget

Arquivo: `src/widget/src/image.rs`

```rust
pub struct Image {
    handle: ImageHandle,
    width: Option<f32>,
    height: Option<f32>,
}
```

---

## Etapa 3 — Z-index explícito

### 3.1 StyleProps

Arquivo: `src/src/element/style.rs`

- Adicionar `z_index: i32` em `StyleProps` (padrão `0`)
- Setter `.z_index(i32)` nos builders

### 3.2 Paint pass — ordenação por z-index

Arquivo: `src/src/paint/mod.rs`

**Estratégia:**

1. Paint pass normal (pré-ordem) para todos os nós com `z_index == 0`
2. Coletar nós com `z_index != 0` durante a travessia em um `Vec<(i32, NodePath)>`
3. Ordenar por `z_index` crescente
4. Pintar cada um em seguida (os de z-index alto ficam por cima)

Nós com z-index formam um novo stacking context — seus filhos são pintados com eles, não na ordem do documento.

---

## Etapa 4 — Widget `Select`

**Depende de:** Z-index (Etapa 3) concluído.

### 4.1 Estrutura

Arquivo: `src/widget/src/select.rs`

```rust
pub struct Select<T: Clone + PartialEq + 'static> {
    value: Signal<T>,
    options: Vec<(T, String)>,
    on_change: Option<Rc<dyn Fn(T)>>,
    width: Option<f32>,
}
```

### 4.2 Estado interno

`Select` é um `Component` com:

- `open: Signal<bool>` — dropdown aberto/fechado
- `hovered_index: Signal<Option<usize>>` — item com hover no dropdown

### 4.3 Renderização

- **Trigger** (sempre visível): `Prim` com label do valor atual + ícone de chevron. Click → `open.set(true)`
- **Dropdown** (quando `open`): `Prim` com `z_index: 100`, posicionado abaixo do trigger, com lista de opções
  - Cada opção: `Prim` com `on_click` que chama `on_change` e fecha o dropdown
  - Hover visual em cada opção via `on_hover_enter` / `on_hover_leave`

### 4.4 Fechar ao clicar fora

- Quando `open`, o Select registra um handler global de click na platform layer
- Se o click não atingiu o dropdown (hit-test), chamar `open.set(false)`
- Implementar via `GlobalClickHandler: Vec<Rc<dyn Fn(Point) -> bool>>` no `AppState` — retorna `true` se consumiu o evento

---

## Etapa 5 — Widget `Slider`

**Depende de:** Hover (V0.2) concluído. Independente de Z-index.

### 5.1 Estrutura

Arquivo: `src/widget/src/slider.rs`

```rust
pub struct Slider {
    value: Signal<f32>,
    min: f32,
    max: f32,
    step: Option<f32>,
    on_change: Option<Rc<dyn Fn(f32)>>,
    width: Option<f32>,
}
```

### 5.2 Renderização

- **Track**: `Prim` horizontal com background secundário, `height: 4px`, `border_radius: 2px`
- **Fill**: `Prim` dentro do track, largura proporcional a `(value - min) / (max - min)`, cor accent
- **Thumb**: `Prim` circular, `width: 16px, height: 16px`, posicionado absolutamente no track

### 5.3 Interação de drag

O `Slider` usa `on_hover_enter` + `on_key_down` (← →) como atalhos, mas o drag é o modo principal:

- `on_click` no track: calcular valor pela posição X do click relativa ao track
- Drag: mouse down no thumb → `AppState` entra em modo "drag ativo" → `CursorMoved` atualiza valor continuamente → mouse up encerra drag

> Drag requer que a platform layer mantenha estado de "botão pressionado + nó capturando" — adicionar `mouse_capture: Option<NodePath>` em `AppState`.

---

## Etapa 6 — Scrollbar visual

**Depende de:** Z-index (Etapa 3) e Scroll (V0.3) concluídos.

- Atualizar widget `Scroll` em `lemon::widget` para renderizar a scrollbar como `Prim` com `z_index: 1`
- Remover a implementação provisória de V0.3 (se tiver sido feita sem z-index)
- Thumb da scrollbar suporta drag (mesmo mecanismo de `mouse_capture` do Slider)

---

## Etapa 7 — Exemplo `examples/rich`

Demonstra V0.4 completo: imagem carregada de arquivo, `Select` de categoria, `Slider` de quantidade, lista de resultados em `Scroll`.

Arquivo: `examples/rich/src/main.rs` + `examples/rich/Cargo.toml`

---

## Critério de done

- [ ] `cargo test --workspace` — sem falhas
- [ ] `cargo clippy --workspace --all-targets -- -D warnings` — sem warnings
- [ ] `Image` renderiza PNG/JPEG a partir de arquivo
- [ ] `z_index` faz nós renderizarem acima de outros
- [ ] `Select` abre dropdown, seleciona opção, fecha ao clicar fora
- [ ] `Slider` atualiza valor via click e drag
- [ ] Scrollbar visual proporcional e arrastável
- [ ] `cargo run -p rich` — exemplo funcional
