# Plano de Implementação — V0.3 (Real App Core)

**Spec de referência:** `2026-05-17-post-v1-roadmap-design.md`  
**Pré-requisito:** V0.2 completo (lemon::widget extraído, Keyboard e Hover implementados)  
**Critério de conclusão:** `cargo test --workspace` + `cargo clippy --workspace --all-targets -- -D warnings` passam. Um exemplo de formulário funcional com TextInput + Scroll.

---

## Ordem de execução

`overflow:hidden` → `Scroll` → `TextInput`  
Clipping é pré-requisito do Scroll. TextInput é independente de Scroll mas depende de Keyboard (V0.2).

---

## Etapa 1 — `overflow: hidden` / Clipping

### 1.1 StyleProps

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

```rust
#[derive(Clone, PartialEq, Default)]
pub enum Overflow {
    #[default]
    Visible,
    Hidden,
}
```

- Adicionar campo `overflow: Overflow` em `StyleProps`
- Adicionar setter `.overflow(Overflow::Hidden)` nos builders de `Prim` / `Box_`

### 1.2 Diff + Patch

- `UpdateStyle` já propaga `StyleProps` inteiro — nenhuma mudança necessária no diff
- Verificar que `RetainedNode.style` armazena `overflow` corretamente

### 1.3 Paint pass

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

- Antes de pintar filhos de um nó com `overflow: Hidden`:
  - `scene.push_layer(Mix::Clip, 1.0, Affine::IDENTITY, &clip_rect)`
  - Pintar filhos normalmente
  - `scene.pop_layer()`
- `clip_rect` = rect do nó no `LayoutMap` (em coordenadas físicas com `scale_factor`)

---

## Etapa 2 — Widget `Scroll`

**Depende de:** Etapa 1 concluída.

### 2.1 Estrutura do widget

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

```rust
pub struct Scroll {
    child: Element,
}

impl Scroll {
    pub fn new(child: impl Into<Element>) -> Self { ... }
    pub fn into_element(self) -> Element { ... }
}
```

O widget `Scroll` renderiza como um `Prim` externo (viewport) com `overflow: Hidden` + um `Prim` interno (conteúdo) com `translate_y` aplicado via `use_signal`.

### 2.2 Scroll offset como estado interno

- `Scroll` é um `Component` que encapsula um `Signal<f64>` de offset vertical
- Layout: viewport tem tamanho fixo (herdado do pai via flex); conteúdo tem `height: auto` (tamanho natural)
- O `Prim` interno usa `margin_top: -offset` ou `transform: translate(0, -offset)` para deslocar o conteúdo

> **Nota de implementação:** Taffy não suporta `transform` — usar `margin_top` negativo é o caminho mais simples para V0.3. Uma solução com `Affine::translate` no paint pass pode vir depois.

### 2.3 Platform — eventos de scroll

Arquivo: `src/src/platform/window.rs`

- Processar `WindowEvent::MouseWheel` do winit
- Hit-test para encontrar o `Scroll` mais próximo sob o cursor (nó com handler de scroll)
- Adicionar `on_scroll: Option<Rc<dyn Fn(f64)>>` em `EventHandlers`
- `Scroll` widget registra `on_scroll` para atualizar seu `Signal<f64>` de offset
- Limitar offset entre `0.0` e `max(0.0, content_height - viewport_height)`

### 2.4 Scrollbar visual

- Renderizada como `Prim` filho posicionado absolutamente dentro do viewport (ou via z-index quando disponível em V0.4)
- Altura proporcional a `viewport_height / content_height`
- Posição Y proporcional a `offset / (content_height - viewport_height)`
- Visível apenas quando `content_height > viewport_height`

---

## Etapa 3 — Widget `TextInput`

**Depende de:** V0.2 Keyboard completo.

### 3.1 Estrutura do widget

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

```rust
pub struct TextInput {
    value: Signal<String>,
    on_change: Option<Rc<dyn Fn(String)>>,
    placeholder: String,
    width: Option<f32>,
}
```

Props:
- `value: Signal<String>` — fonte da verdade do texto
- `on_change: Fn(String)` — callback ao mudar (pode ser omitido se `value` é diretamente mutável)
- `placeholder: &str` — texto cinza quando vazio
- `.width(f32)` — largura explícita (padrão: fill available)

### 3.2 Estado interno do componente

`TextInput` é um `Component` que mantém via `Cx`:

- `cursor_pos: Signal<usize>` — posição do cursor em bytes/chars
- `selection: Signal<Option<(usize, usize)>>` — range selecionado (start, end)
- `focused: Signal<bool>` — se o campo tem foco

### 3.3 Renderização

O `TextInput` renderiza como um `Prim` com borda, background e um `TextNode` filho.

- Borda padrão cinza; borda azul/accent quando `focused`
- Placeholder renderizado como `TextNode` com cor `text_secondary` quando `value` vazio
- Cursor renderizado como uma linha vertical fina (`Prim` de 1–2px) posicionado via Parley `hit_test_point`

### 3.4 Edição de texto via teclado

Handler `on_key_down` no `Prim` do TextInput:

| Tecla | Ação |
|-------|------|
| Char (printable) | Inserir no `cursor_pos`; avançar cursor |
| Backspace | Remover char anterior; recuar cursor |
| Delete | Remover char posterior |
| ← / → | Mover cursor; com Shift → expandir seleção |
| Home / End | Ir ao início/fim da linha |
| Ctrl+A | Selecionar tudo |
| Ctrl+C | Copiar seleção para clipboard |
| Ctrl+V | Colar do clipboard |
| Ctrl+X | Recortar seleção |

Clipboard: usar `winit::event_loop::EventLoopProxy` ou `arboard` crate para acesso ao clipboard do sistema.

### 3.5 Seleção via mouse

- `on_click`: posicionar cursor via `Parley::hit_test_point(click_pos)`
- Drag (mouse down + move + up): expandir seleção

---

## Etapa 4 — Exemplo `examples/form`

Demonstra V0.3 completo: formulário com dois `TextInput`, um `Scroll` com lista de resultados e um `Button` de submit.

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

---

## Critério de done

- [ ] `cargo test --workspace` — sem falhas
- [ ] `cargo clippy --workspace --all-targets -- -D warnings` — sem warnings
- [ ] `overflow: Hidden` recorta conteúdo filho visualmente
- [ ] `Scroll` rola conteúdo com MouseWheel; scrollbar visível e proporcional
- [ ] `TextInput` aceita texto digitado, Backspace, setas, Ctrl+A/C/V
- [ ] Foco visual do `TextInput` muda ao clicar
- [ ] `cargo run -p form` — formulário funcional
