# Whiteboard

> Superficie de dibujo libre a mano, con trazo suavizado, color y grosor configurables.

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

# Whiteboard

`Whiteboard` es una superficie de **dibujo libre**: el usuario arrastra el dedo/puntero y el componente registra los puntos, suaviza el trazo (promedio ponderado de los vecinos) y lo renderiza sobre una `Image` interna. Es la base para captura de firma, anotaciones a mano y pequeños bocetos. Cada trazo concluido (en el `onTouchUp`) se "quema" en la imagen, de modo que el historial se acumula sin reprocesar todos los puntos en cada cuadro.

Tú controlas el color de la pluma (`setPenColor`), el grosor (campo `thick`) y puedes limpiar (`clear()`) o exportar/definir la imagen de fondo (`getImage`/`setImage`).

> 🟢 · **Paquete:** `dev.crossfly.ui` · **Extiende:** `Component` · **Estilo:** `ComponentStyle` (fondo blanco por defecto)

## Cuándo usar
- Capturar una firma o un dibujo a mano libre.
- Anotar sobre una imagen existente (pásala vía `setImage`).

## Cuándo NO usar
- Mostrar/graficar datos estructurados → usa [`chartview`](/docs/ui/widgets/chartview).

## Apariencia
Fondo blanco (`backColor`) del tamaño del componente. Los trazos son líneas suavizadas con grosor `Styles.fontHeight/3` (grueso) o `Styles.fontHeight/10` (fino), en el color de la pluma (con alpha forzado a opaco). El componente tiene `hasPan(true)`, es decir, captura el gesto de arrastrar para dibujar.

## Ejemplo completo (copiar, pegar y ejecutar)
`Whiteboard` es un componente común: entra directo en la pantalla con `add(LAY_FILL, ...)` para ocupar el cuerpo. El ejemplo reutiliza el uso básico (pluma azul, trazo grueso) y agrega un botón para limpiar.

```java
package com.exemplo;

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

/** Pantalla principal ejecutable. Apunta el launcher (classname) y corre: 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 (el alpha se fuerza a opaco en el dibujado)
      board.thick = true;            // trazo grueso

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

      add(LAY_FILL, board);          // ocupa el cuerpo de la pantalla
   }
}
```

> Los fragmentos siguientes muestran **solo el núcleo** (lo que va dentro de `onShow()`). Para ejecutarlos, colócalos en el esqueleto `MainWindow` de arriba.

## Uso básico
```java
Whiteboard board = new Whiteboard();
board.setPenColor(0x0000FF);   // azul (el alpha se fuerza a opaco en el dibujado)
board.thick = true;            // trazo grueso
container.add(LAY_FILL, board);

// En otro lugar, un botón para limpiar:
btnLimpar.onAction(c -> board.clear());
```

## Constructores
| Constructor | Descripción |
|---|---|
| `Whiteboard()` | Crea la superficie con fondo blanco y `hasPan(true)` (lista para dibujar). |

## Propiedades y personalización
| Propiedad / método | Tipo | Predeterminado | Qué hace |
|---|---|---|---|
| `setPenColor(int)` | `void` | `BLACK` | Define el color de la pluma (el dibujo fuerza alpha opaco). |
| `getPenColor()` | `int` | — | Retorna el color actual de la pluma. |
| `thick` | `boolean` (campo) | `false` | `true` = trazo grueso (`fontHeight/3`); `false` = fino (`fontHeight/10`). |
| `clear()` | `void` | — | Limpia la imagen y los puntos acumulados. |
| `getImage()` | `Image` | `null` | Retorna la imagen de dibujo (puede ser `null` si no se dibujó nada). |
| `setImage(Image)` | `void` | — | Define la imagen de fondo/base (pasa `null` para superficie vacía). `throws ImageException`. |
| `Whiteboard.debugPoints` | `boolean` estático | `false` | Depuración: muestra el conteo de puntos/tiempo de renderizado. |

## Callbacks y eventos
`Whiteboard` trata los gestos internamente; no expone un callback de "trazo concluido".

| Evento (interno) | Comportamiento |
|---|---|
| `onTouchDown(x,y,button)` | Inicia un nuevo trazo y graba el primer punto. |
| `onPan(x,y,dx,dy)` | Acumula puntos mientras el usuario arrastra y repinta. |
| `onTouchUp(x,y,button)` | Finaliza el trazo y "quema" el dibujo en la `Image` interna. |

## Estados
- **dibujando**: entre `onTouchDown` y `onTouchUp`, los puntos quedan en un buffer temporal y son suavizados/renderizados en cada `onPan`.
- **consolidado**: en el `onTouchUp`, el trazo se graba en la `Image` y el buffer se limpia (el costo por cuadro no crece con el total de trazos).

### Panel de firma con selección de color y exportación
```java
Whiteboard assinatura = new Whiteboard();
assinatura.thick = false;                 // trazo fino para firmar
container.add(LAY_FILL, assinatura);

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

// Al confirmar, recupera la imagen resultante:
btnOk.onAction(c -> {
   Image desenho = assinatura.getImage();   // puede ser null si nada fue dibujado
   if (desenho != null)
      salvarAssinatura(desenho);
});
```

## Buenas prácticas
- Prefiere `thick = false` para firmas (trazo más preciso) y `thick = true` para bocetos amplios.
- Verifica siempre `getImage() != null` antes de exportar: si el usuario no dibujó nada, la imagen aún es `null`.
- Ofrece un botón de `clear()` — no hay "deshacer" por trazo.

## Errores comunes
- Pasar un color con alpha explícito esperando transparencia: el dibujo **fuerza alpha opaco** (`0xFF000000 | cor`).
- Esperar un callback de trazo: usa los métodos (`getImage`) tras la interacción; no hay `onAction` de conclusión de trazo.

## Ver también
- Guía del área: [`ui/`](/docs/ui) · Recetas: [`cookbook/`](/docs/cookbook)
- Concepto (imágenes, primitivas gráficas): [`concepts/`](/docs/concepts)
- Componentes relacionados: [`chartview`](/docs/ui/widgets/chartview)
