# `Plushie.Widget.Canvas`
[🔗](https://github.com/plushie-ui/plushie-elixir/blob/v0.6.0/lib/plushie/widget/canvas.ex#L1)

Canvas for drawing shapes, organized into named layers.

Layers are a map of layer names to shape lists. Each layer maps to an iced
`Cache` on the Rust side -- only changed layers are re-tessellated. This
prevents performance footguns when rendering thousands of shapes in a stable
layer.

Shape descriptors are plain maps with string keys. Use `Plushie.Canvas.Shape`
for convenience builders, or construct maps directly.

## Do-block form

The `canvas` macro in `Plushie.UI` supports a do-block form that collects
layers declaratively:

    import Plushie.UI

    canvas "chart", width: 400, height: 300 do
      layer "grid" do
        rect(0, 0, 400, 300, stroke: "#eee")
      end
      layer "data" do
        for bar <- bars do
          rect(bar.x, bar.y, bar.w, bar.h, fill: bar.color)
        end
      end
    end

## Props

- `layers` (map of string => list of maps) -- named layers of shape
  descriptors. Each layer is independently cached. Shapes within a layer
  are drawn in order. Layers are drawn in alphabetical order by name.
- `shapes` (list of maps) -- flat list of shape descriptors. Convenience
  alternative to `layers` for canvases that don't need named layers.
- `width` (length) -- canvas width. Default: fill. See `Plushie.Type.Length`.
- `height` (length) -- canvas height. Default: 200px.
- `background` (color) -- canvas background color. See `Plushie.Type.Color`.
- `interactive` (boolean) -- enables all mouse event handlers. Default: false.
- `on_press` (boolean) -- enable mouse press events. Default: false.
- `on_release` (boolean) -- enable mouse release events. Default: false.
- `on_move` (boolean) -- enable mouse move events. Default: false.
- `on_scroll` (boolean) -- enable mouse scroll events. Default: false.
- `alt` (string) -- accessible label for the canvas. Sits outside the
  `a11y` object. See "Widget-specific accessibility props" in
  `docs/reference/accessibility.md`.
- `description` (string) -- extended accessible description for the
  canvas. Sits outside the `a11y` object.
- `a11y` (map) -- accessibility overrides. See `Plushie.Type.A11y`.

## Shape types

Shapes are plain maps. See `Plushie.Canvas.Shape` for builder functions.

- `%{type: "rect", x: x, y: y, w: w, h: h}` -- rectangle.
- `%{type: "circle", x: x, y: y, r: r}` -- circle.
- `%{type: "line", x1: x1, y1: y1, x2: x2, y2: y2}` -- line.
- `%{type: "text", x: x, y: y, content: text}` -- text.
- `%{type: "path", commands: [...]}` -- arbitrary path.
- `%{type: "image", source: path, x: x, y: y, w: w, h: h}` -- image.
- `%{type: "svg", source: path, x: x, y: y, w: w, h: h}` -- SVG.

All shapes accept optional `fill` (hex color or gradient) and `stroke` fields.

## Events

Canvas background events (coordinate-level, unified pointer types):

- `%WidgetEvent{type: :press, id: id, data: %{x: x, y: y, button: button, pointer: pointer, modifiers: mods}}`
- `%WidgetEvent{type: :release, id: id, data: %{x: x, y: y, button: button, pointer: pointer, modifiers: mods}}`
- `%WidgetEvent{type: :move, id: id, data: %{x: x, y: y, pointer: pointer, modifiers: mods}}`
- `%WidgetEvent{type: :scroll, id: id, data: %{x: x, y: y, delta_x: dx, delta_y: dy, pointer: pointer, modifiers: mods}}`
- `%WidgetEvent{type: :enter, id: id}`
- `%WidgetEvent{type: :exit, id: id}`

Interactive shape events (semantic, from shapes with `interactive` field):

- `%WidgetEvent{type: :click, id: element_id, scope: [canvas_id | ...]}`
- `%WidgetEvent{type: :enter, id: id}`
- `%WidgetEvent{type: :exit, id: id}`
- `%WidgetEvent{type: :drag, id: id, data: %{x: x, y: y, delta_x: dx, delta_y: dy}}`
- `%WidgetEvent{type: :drag_end, id: id, data: %{x: x, y: y}}`
- `%WidgetEvent{type: :focused, id: id}`
- `%WidgetEvent{type: :blurred, id: id}`
- `%WidgetEvent{type: :key_press, id: id, data: %{key: key, modifiers: mods, text: text}}`
- `%WidgetEvent{type: :key_release, id: id, data: %{key: key, modifiers: mods}}`

Canvas element clicks arrive as regular `:click` events with the element ID
as the event `id` and the canvas ID in the `scope`. Other element events use
standard generic event families.

# `canvas_shape`

```elixir
@type canvas_shape() ::
  Plushie.Canvas.Shape.Rect.t()
  | Plushie.Canvas.Shape.Circle.t()
  | Plushie.Canvas.Shape.Line.t()
  | Plushie.Canvas.Shape.CanvasText.t()
  | Plushie.Canvas.Shape.Path.t()
  | Plushie.Canvas.Shape.CanvasImage.t()
  | Plushie.Canvas.Shape.CanvasSvg.t()
  | Plushie.Canvas.Shape.Group.t()
  | Plushie.Canvas.Shape.Translate.t()
  | Plushie.Canvas.Shape.Rotate.t()
  | Plushie.Canvas.Shape.Scale.t()
  | Plushie.Canvas.Shape.Clip.t()
  | map()
```

A canvas shape descriptor (struct or plain map).

# `option`

```elixir
@type option() ::
  {:layers, %{required(String.t()) =&gt; [canvas_shape()]}}
  | {:shapes, [canvas_shape()]}
  | {:width, Plushie.Type.Length.t()}
  | {:height, Plushie.Type.Length.t()}
  | {:background, Plushie.Type.Color.input()}
  | {:interactive, boolean()}
  | {:on_press, boolean()}
  | {:on_release, boolean()}
  | {:on_move, boolean()}
  | {:on_scroll, boolean()}
  | {:alt, String.t()}
  | {:description, String.t()}
  | {:event_rate, pos_integer()}
  | {:a11y, Plushie.Type.A11y.t() | map() | keyword()}
```

# `t`

```elixir
@type t() :: %Plushie.Widget.Canvas{
  a11y: Plushie.Type.A11y.t() | nil,
  alt: String.t() | nil,
  arrow_mode: String.t() | nil,
  background: Plushie.Type.Color.t() | nil,
  description: String.t() | nil,
  event_rate: pos_integer() | nil,
  height: Plushie.Type.Length.t() | nil,
  id: String.t(),
  interactive: boolean() | nil,
  layers: %{required(String.t()) =&gt; [canvas_shape()]} | nil,
  on_move: boolean() | nil,
  on_press: boolean() | nil,
  on_release: boolean() | nil,
  on_scroll: boolean() | nil,
  role: String.t() | nil,
  shapes: [canvas_shape()] | nil,
  width: Plushie.Type.Length.t() | nil
}
```

# `a11y`

```elixir
@spec a11y(canvas :: t(), a11y :: Plushie.Type.A11y.t() | map() | keyword()) :: t()
```

Sets accessibility annotations.

# `alt`

```elixir
@spec alt(canvas :: t(), alt :: String.t()) :: t()
```

Sets the accessible label for the canvas.

# `arrow_mode`

```elixir
@spec arrow_mode(canvas :: t(), mode :: String.t()) :: t()
```

Sets the arrow key navigation mode ("wrap", "clamp", "linear", "none").

# `background`

```elixir
@spec background(canvas :: t(), background :: Plushie.Type.Color.input()) :: t()
```

Sets the canvas background color. Accepts a hex string or named color atom.

# `build`

```elixir
@spec build(canvas :: t()) :: Plushie.Widget.ui_node()
```

Converts this canvas struct to a `ui_node()` map via the `Plushie.Widget` protocol.

# `description`

```elixir
@spec description(canvas :: t(), description :: String.t()) :: t()
```

Sets an extended accessible description for the canvas.

# `event_rate`

```elixir
@spec event_rate(canvas :: t(), rate :: pos_integer()) :: t()
```

Sets the maximum event rate (events per second) for this widget's coalescable events.

# `height`

```elixir
@spec height(canvas :: t(), height :: Plushie.Type.Length.t()) :: t()
```

Sets the canvas height.

# `interactive`

```elixir
@spec interactive(canvas :: t(), interactive :: boolean()) :: t()
```

Sets whether all mouse event handlers are enabled.

# `layer`

```elixir
@spec layer(canvas :: t(), name :: String.t(), shapes :: [canvas_shape()]) :: t()
```

Adds a single named layer to the canvas. Merges with existing layers.

# `layers`

```elixir
@spec layers(canvas :: t(), layers :: %{required(String.t()) =&gt; [canvas_shape()]}) ::
  t()
```

Sets the layers map (layer name => list of shape descriptors).

# `new`

```elixir
@spec new(id :: String.t(), opts :: [option()]) :: t()
```

Creates a new canvas struct with optional keyword opts.

# `on_move`

```elixir
@spec on_move(canvas :: t(), on_move :: boolean()) :: t()
```

Sets whether mouse move events are enabled.

# `on_press`

```elixir
@spec on_press(canvas :: t(), on_press :: boolean()) :: t()
```

Sets whether mouse press events are enabled.

# `on_release`

```elixir
@spec on_release(canvas :: t(), on_release :: boolean()) :: t()
```

Sets whether mouse release events are enabled.

# `on_scroll`

```elixir
@spec on_scroll(canvas :: t(), on_scroll :: boolean()) :: t()
```

Sets whether mouse scroll events are enabled.

# `role`

```elixir
@spec role(canvas :: t(), role :: String.t()) :: t()
```

Sets the accessible role for the canvas (e.g. "radiogroup", "toolbar").

# `shapes`

```elixir
@spec shapes(canvas :: t(), shapes :: [canvas_shape()]) :: t()
```

Sets a flat list of shapes (convenience shorthand for unlayered canvases).

# `width`

```elixir
@spec width(canvas :: t(), width :: Plushie.Type.Length.t()) :: t()
```

Sets the canvas width.

# `with_options`

```elixir
@spec with_options(canvas :: t(), opts :: [option()]) :: t()
```

Applies keyword options to an existing canvas struct.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
