# Whiteboard

> Freehand drawing surface, with smoothed strokes and configurable color and thickness.

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

# Whiteboard

`Whiteboard` is a **freehand drawing** surface: the user drags their finger/pointer and the component records the points, smooths the stroke (weighted average of neighbors), and renders it onto an internal `Image`. It is the foundation for signature capture, handwritten notes, and small sketches. Each completed stroke (on `onTouchUp`) is "burned" into the image, so that the history accumulates without reprocessing all points on every frame.

You control the pen color (`setPenColor`), the thickness (the `thick` field), and you can clear (`clear()`) or export/set the background image (`getImage`/`setImage`).

> 🟢 · **Package:** `dev.crossfly.ui` · **Extends:** `Component` · **Style:** `ComponentStyle` (white background by default)

## When to use
- Capture a signature or a freehand drawing.
- Annotate over an existing image (pass it in via `setImage`).

## When NOT to use
- Display/plot structured data → use [`chartview`](/docs/ui/widgets/chartview).

## Appearance
White background (`backColor`) the size of the component. The strokes are smoothed lines with thickness `Styles.fontHeight/3` (thick) or `Styles.fontHeight/10` (thin), in the pen color (with alpha forced to opaque). The component has `hasPan(true)`, meaning it captures the drag gesture to draw.

## Complete example (copy, paste, and run)
`Whiteboard` is an ordinary component: it goes straight onto the screen with `add(LAY_FILL, ...)` to occupy the body. The example reuses the basic usage (blue pen, thick stroke) and adds a button to clear.

```java
package com.exemplo;

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

/** Runnable main screen. Point the launcher (classname) and run: 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);   // blue (the alpha is forced to opaque on draw)
      board.thick = true;            // thick stroke

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

      add(LAY_FILL, board);          // occupies the body of the screen
   }
}
```

> The snippets below show **only the core** (what goes inside `onShow()`). To run them, place them in the `MainWindow` skeleton above.

## Basic usage
```java
Whiteboard board = new Whiteboard();
board.setPenColor(0x0000FF);   // blue (the alpha is forced to opaque on draw)
board.thick = true;            // thick stroke
container.add(LAY_FILL, board);

// Elsewhere, a button to clear:
btnLimpar.onAction(c -> board.clear());
```

## Constructors
| Constructor | Description |
|---|---|
| `Whiteboard()` | Creates the surface with a white background and `hasPan(true)` (ready for drawing). |

## Properties and customization
| Property / method | Type | Default | What it does |
|---|---|---|---|
| `setPenColor(int)` | `void` | `BLACK` | Sets the pen color (drawing forces opaque alpha). |
| `getPenColor()` | `int` | — | Returns the current pen color. |
| `thick` | `boolean` (field) | `false` | `true` = thick stroke (`fontHeight/3`); `false` = thin (`fontHeight/10`). |
| `clear()` | `void` | — | Clears the image and the accumulated points. |
| `getImage()` | `Image` | `null` | Returns the drawing image (may be `null` if nothing was drawn). |
| `setImage(Image)` | `void` | — | Sets the background/base image (pass `null` for an empty surface). `throws ImageException`. |
| `Whiteboard.debugPoints` | `boolean` static | `false` | Debug: displays point count/render time. |

## Callbacks and events
`Whiteboard` handles gestures internally; it does not expose a "stroke completed" callback.

| Event (internal) | Behavior |
|---|---|
| `onTouchDown(x,y,button)` | Starts a new stroke and records the first point. |
| `onPan(x,y,dx,dy)` | Accumulates points while the user drags and repaints. |
| `onTouchUp(x,y,button)` | Finalizes the stroke and "burns" the drawing into the internal `Image`. |

## States
- **drawing**: between `onTouchDown` and `onTouchUp`, the points stay in a temporary buffer and are smoothed/rendered on each `onPan`.
- **consolidated**: on `onTouchUp`, the stroke is written into the `Image` and the buffer is cleared (the per-frame cost does not grow with the total number of strokes).

### Signature panel with color selection and export
```java
Whiteboard assinatura = new Whiteboard();
assinatura.thick = false;                 // thin stroke for signing
container.add(LAY_FILL, assinatura);

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

// On confirm, retrieve the resulting image:
btnOk.onAction(c -> {
   Image desenho = assinatura.getImage();   // may be null if nothing was drawn
   if (desenho != null)
      salvarAssinatura(desenho);
});
```

## Best practices
- Prefer `thick = false` for signatures (more precise stroke) and `thick = true` for broad sketches.
- Always check `getImage() != null` before exporting: if the user drew nothing, the image is still `null`.
- Offer a `clear()` button — there is no per-stroke "undo".

## Common mistakes
- Passing a color with explicit alpha expecting transparency: drawing **forces opaque alpha** (`0xFF000000 | cor`).
- Expecting a stroke callback: use the methods (`getImage`) after the interaction; there is no stroke-completion `onAction`.

## See also
- Area guide: [`ui/`](/docs/ui) · Recipes: [`cookbook/`](/docs/cookbook)
- Concept (images, graphics primitives): [`concepts/`](/docs/concepts)
- Related components: [`chartview`](/docs/ui/widgets/chartview)
