# Whiteboard

> Superfície de desenho livre à mão, com traço suavizado, cor e espessura configuráveis.

Source: https://crossfly.dev/pt/docs/ui/widgets/whiteboard — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# Whiteboard

`Whiteboard` é uma superfície de **desenho livre**: o usuário arrasta o dedo/ponteiro e o componente registra os pontos, suaviza o traço (média ponderada dos vizinhos) e o renderiza sobre uma `Image` interna. É a base para captura de assinatura, anotações à mão e pequenos rascunhos. Cada traço concluído (no `onTouchUp`) é "queimado" na imagem, de modo que o histórico acumula sem reprocessar todos os pontos a cada quadro.

Você controla a cor da caneta (`setPenColor`), a espessura (campo `thick`) e pode limpar (`clear()`) ou exportar/definir a imagem de fundo (`getImage`/`setImage`).

> 🟢 · **Pacote:** `dev.crossfly.ui` · **Estende:** `Component` · **Estilo:** `ComponentStyle` (fundo branco por default)

## Quando usar
- Capturar uma assinatura ou um desenho à mão livre.
- Anotar sobre uma imagem existente (passe-a via `setImage`).

## Quando NÃO usar
- Exibir/plotar dados estruturados → use [`chartview`](/docs/ui/widgets/chartview).

## Aparência
Fundo branco (`backColor`) do tamanho do componente. Os traços são linhas suavizadas com espessura `Styles.fontHeight/3` (grosso) ou `Styles.fontHeight/10` (fino), na cor da caneta (com alpha forçado para opaco). O componente tem `hasPan(true)`, ou seja, captura o gesto de arrastar para desenhar.

## Exemplo completo (copiar, colar e rodar)
`Whiteboard` é um componente comum: entra direto na tela com `add(LAY_FILL, ...)` para ocupar o corpo. O exemplo reutiliza o uso básico (caneta azul, traço grosso) e adiciona um botão para limpar.

```java
package com.exemplo;

import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;

/** Tela principal executável. Aponte o launcher (classname) e rode: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Whiteboard board = new Whiteboard();
      board.setPenColor(0x0000FF);   // azul (o alpha é forçado para opaco no desenho)
      board.thick = true;            // traço grosso

      Button limpar = new Button("Limpar")
         .onAction(c -> board.clear());
      add(LAY_AUTO, limpar);

      add(LAY_FILL, board);          // ocupa o corpo da tela
   }
}
```

> Os trechos abaixo mostram **só o miolo** (o que vai dentro de `onShow()`). Para rodar, coloque-os no esqueleto `MainWindow` acima.

## Uso básico
```java
Whiteboard board = new Whiteboard();
board.setPenColor(0x0000FF);   // azul (o alpha é forçado para opaco no desenho)
board.thick = true;            // traço grosso
container.add(LAY_FILL, board);

// Em outro lugar, um botão para limpar:
btnLimpar.onAction(c -> board.clear());
```

## Construtores
| Construtor | Descrição |
|---|---|
| `Whiteboard()` | Cria a superfície com fundo branco e `hasPan(true)` (pronta para desenho). |

## Propriedades e personalização
| Propriedade / método | Tipo | Padrão | O que faz |
|---|---|---|---|
| `setPenColor(int)` | `void` | `BLACK` | Define a cor da caneta (o desenho força alpha opaco). |
| `getPenColor()` | `int` | — | Retorna a cor atual da caneta. |
| `thick` | `boolean` (campo) | `false` | `true` = traço grosso (`fontHeight/3`); `false` = fino (`fontHeight/10`). |
| `clear()` | `void` | — | Limpa a imagem e os pontos acumulados. |
| `getImage()` | `Image` | `null` | Retorna a imagem de desenho (pode ser `null` se nada foi desenhado). |
| `setImage(Image)` | `void` | — | Define a imagem de fundo/base (passe `null` para superfície vazia). `throws ImageException`. |
| `Whiteboard.debugPoints` | `boolean` estático | `false` | Depuração: exibe contagem de pontos/tempo de renderização. |

## Callbacks e eventos
`Whiteboard` trata os gestos internamente; não expõe um callback de "traço concluído".

| Evento (interno) | Comportamento |
|---|---|
| `onTouchDown(x,y,button)` | Inicia um novo traço e grava o primeiro ponto. |
| `onPan(x,y,dx,dy)` | Acumula pontos enquanto o usuário arrasta e repinta. |
| `onTouchUp(x,y,button)` | Finaliza o traço e "queima" o desenho na `Image` interna. |

## Estados
- **desenhando**: entre `onTouchDown` e `onTouchUp`, os pontos ficam em um buffer temporário e são suavizados/renderizados a cada `onPan`.
- **consolidado**: no `onTouchUp`, o traço é gravado na `Image` e o buffer é limpo (o custo por quadro não cresce com o total de traços).

### Painel de assinatura com seleção de cor e exportação
```java
Whiteboard assinatura = new Whiteboard();
assinatura.thick = false;                 // traço fino para assinar
container.add(LAY_FILL, assinatura);

btnPreto.onAction(c -> assinatura.setPenColor(0x000000));
btnAzul.onAction (c -> assinatura.setPenColor(0x0000FF));
btnLimpar.onAction(c -> assinatura.clear());

// Ao confirmar, recupere a imagem resultante:
btnOk.onAction(c -> {
   Image desenho = assinatura.getImage();   // pode ser null se nada foi desenhado
   if (desenho != null)
      salvarAssinatura(desenho);
});
```

## Boas práticas
- Prefira `thick = false` para assinaturas (traço mais preciso) e `thick = true` para rascunhos amplos.
- Sempre verifique `getImage() != null` antes de exportar: se o usuário não desenhou nada, a imagem ainda é `null`.
- Ofereça um botão de `clear()` — não há "desfazer" por traço.

## Erros comuns
- Passar uma cor com alpha explícito esperando transparência: o desenho **força alpha opaco** (`0xFF000000 | cor`).
- Esperar um callback de traço: use os métodos (`getImage`) após a interação; não há `onAction` de conclusão de traço.

## Veja também
- Guia da área: [`ui/`](/docs/ui) · Receitas: [`cookbook/`](/docs/cookbook)
- Conceito (imagens, primitivas gráficas): [`concepts/`](/docs/concepts)
- Componentes relacionados: [`chartview`](/docs/ui/widgets/chartview)
