Plushie.Widget behaviour (Plushie v0.7.1)

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

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

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

Callbacks

Path to the Rust crate relative to the package root.

Full Rust constructor expression for the widget.

Node type atoms this widget handles.

Functions

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.