Search documentation of Plushie

# Plushie.Widget behaviour (Plushie v0.7.2)

Copy Markdown View Source

Macro-based DSL for declaring Plushie widgets.

Supports two kinds of widget:

- `:native_widget` - backed by a Rust crate implementing the `PlushieWidget` trait. Requires `rust_crate` and `rust_constructor` declarations.
- `:widget` - pure Elixir widget. Features are detected at compile time based on what callbacks are defined:
  - Has `state` declarations -> stateful (deferred view, state persistence via the runtime).
  - No `state` -> stateless (immediate view in `new/2`).
  - Has `handle_event/2` -> participates in event dispatch.
  - Has `subscribe/2` -> widget-scoped subscriptions.

## Usage

    defmodule MyApp.Gauge do
      use Plushie.Widget, :native_widget

      widget :gauge

      field :value, :float
      field :min, :float, default: 0
      field :max, :float, default: 100
      field :color, Plushie.Type.Color, default: :blue
      field :width, Plushie.Type.Length
      field :height, Plushie.Type.Length

      rust_crate "native/my_gauge"
      rust_constructor "my_gauge::GaugeWidget::new()"

      event :value_changed, fields: [value: :float]
      command :set_value, value: :float
    end

## Generated code

The macro generates:

- `type_names/0` - returns `[:gauge]` (from the `widget` declaration)
- `native_crate/0` - returns the `rust_crate` path (native_widget only)
- `rust_constructor/0` - returns the Rust expression (native_widget only)
- `new/2` - creates a `%Module{}` struct ()
- Setter functions per field for pipeline composition
- `with_options/2` - applies keyword options via setters
- `build/1` - converts the struct to a `ui_node()` map
- `@type t`, `@type option` - typespecs for dialyzer
- `Plushie.Widget` protocol implementation
- `__event_specs__/0`, `__event_spec__/1` - typed event metadata
- Command functions (native_widget only) that wrap `Plushie.Command.widget_command/3`

## Field types

Field types are resolved via `Plushie.Type.resolve/1`. Primitive atom shortcuts (`:integer`, `:float`, `:string`, `:boolean`, `:atom`, `:any`, `:map`), `Plushie.Type` module names (e.g. `Plushie.Type.Color`), and composite forms (`{:list, :string}`) are accepted. Values are stored raw; `Tree.normalize/1` handles wire encoding in a single pass.

## Composite widgets

If the using module defines `view/2` (leaf) or `view/3` (container), `new/2` delegates to it after resolving props:

    defmodule MyApp.LabeledInput do
      use Plushie.Widget

      widget :labeled_input

      field :label, :string

      def view(id, props) do
        import Plushie.UI
        column id: id do
          text(props.label)
        end
      end
    end

### view/2 vs view/3

Use `view/2` for simple widgets:

    def view(id, props) do
      %{id: id, type: "text", props: %{content: props.label}, children: []}
    end

Use `view/3` when the widget has state (declared via `state`). The third argument is the widget's internal state map:

    def view(id, props, state) do
      fill = if state.hover, do: "#ff0", else: "#ccc"
      ...
    end

## Common options

Widgets that support accessibility or event rate limiting declare these as normal fields:

- `field :a11y, Plushie.Type.A11y, merge: true` - accessibility overrides (see `Plushie.Type.A11y`). The `merge: true` option makes the setter merge user values with widget defaults.
- `field :event_rate, :integer` - maximum events per second for coalescable events from this widget.

## Summary

### Types

child()

A child element: either an already-resolved node map or a widget struct.

ui_node()

A UI tree node map. Every widget builder returns this shape.

### Callbacks

native_crate()

Path to the Rust crate relative to the package root.

rust_constructor()

Full Rust constructor expression for the widget.

type_names()

Node type atoms this widget handles.

### Functions

to_node(widget)

Converts a widget struct to a `ui_node()` map via the Tree.Node protocol.

## Types

# child()

    @type child() :: ui_node() | struct()

A child element: either an already-resolved node map or a widget struct.

# ui_node()

    @type ui_node() :: Plushie.Tree.Node.ui_node()

A UI tree node map. Every widget builder returns this shape.

## Callbacks

# native_crate()

(optional)

    @callback native_crate() :: String.t()

Path to the Rust crate relative to the package root.

# rust_constructor()

(optional)

    @callback rust_constructor() :: String.t()

Full Rust constructor expression for the widget.

# type_names()

    @callback type_names() :: [atom()]

Node type atoms this widget handles.

## Functions

# to_node(widget)

    @spec to_node(struct()) :: ui_node()

Converts a widget struct to a `ui_node()` map via the Tree.Node protocol.