Macro-based DSL for declaring Plushie widgets.
Supports two kinds of widget:
:native_widget-- backed by a Rust crate implementing theWidgetExtensiontrait. Requiresrust_crateandrust_constructordeclarations.:widget-- pure Elixir widget. Features are detected at compile time based on what callbacks are defined:- Has
statedeclarations -> stateful (deferred view, state persistence via the runtime). - No
state-> stateless (immediate view innew/2). - Has
handle_event/2-> participates in event dispatch. - Has
subscribe/2-> widget-scoped subscriptions.
- Has
Usage
defmodule MyApp.Gauge do
use Plushie.Widget, :native_widget
widget :gauge
prop :value, :number
prop :min, :number, default: 0
prop :max, :number, default: 100
prop :color, :color, default: :blue
prop :width, :length
prop :height, :length
rust_crate "native/my_gauge"
rust_constructor "my_gauge::GaugeWidget::new()"
event :value_changed, data: [value: :number]
command :set_value, value: :number
endGenerated code
The macro generates:
type_names/0-- returns[:gauge](from thewidgetdeclaration)native_crate/0-- returns therust_cratepath (native_widget only)rust_constructor/0-- returns the Rust expression (native_widget only)new/2-- creates a%Module{}struct ()- Setter functions per prop for pipeline composition
with_options/2-- applies keyword options via settersbuild/1-- converts the struct to aui_node()map@type t,@type option-- typespecs for dialyzerPlushie.Widgetprotocol implementation__event_specs__/0,__event_spec__/1-- typed event metadata- Command functions (native_widget only) that wrap
Plushie.Command.widget_command/3
Prop types
Supported prop types. Values are stored raw; Tree.normalize/1
handles wire encoding in a single pass.
:number,:string,:boolean-- pass through:color-- normalized viaPlushie.Type.Color.cast/1(input casting):length-- pass through (encoded byTree.normalize):padding-- pass through (encoded byTree.normalize):alignment-- pass through (encoded byTree.normalize):font-- pass through:style-- pass through (atom or StyleMap):atom-- pass through (encoded byTree.normalize):map,:any-- pass through{:list, _}-- pass through
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
prop :label, :string
def view(id, props) do
import Plushie.UI
column id: id do
text(props.label)
end
end
endview/2 vs view/3
Use view/2 for simple widgets:
def view(id, props) do
%{id: id, type: "text", props: %{content: props.label}, children: []}
endUse 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"
...
endSpecial options
All widgets automatically support:
:a11y-- accessibility overrides (seePlushie.Type.A11y):event_rate-- maximum events per second for coalescable events from this widget (see the event throttling design doc)
These do not need to be declared via prop -- they are always
available on new/2.
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
Declares a command (native_widget only) with optional typed params.
Declares a typed event emitted by a native or canvas widget.
Declares a prop with name, type, and optional default.
Declares the Rust constructor expression (native_widget only).
Declares the path to the Rust crate (native_widget only).
Declares internal state fields for a stateful widget.
Converts a widget struct to a ui_node() map via the WidgetProtocol.
Declares the widget type name. Pass container: true for container widgets.
Types
A child element: either an already-resolved node map or a widget struct.
@type ui_node() :: Plushie.Widget.WidgetProtocol.ui_node()
A UI tree node map. Every widget builder returns this shape.
Callbacks
Functions
Declares a command (native_widget only) with optional typed params.
Declares a typed event emitted by a native or canvas widget.
Supports three forms:
No payload
event :clearedTyped value (goes in WidgetEvent.value)
event :select, value: :numberStructured data (goes in WidgetEvent.data with atom keys)
event :change, data: [hue: :number, saturation: :number]Block form
event :change do
data do
field :hue, :number
field :saturation, :number
end
endvalue: and data: are mutually exclusive.
Type identifiers can be built-in atoms (:number, :string,
:boolean, :any) or modules implementing Plushie.Event.EventType.
Declares a prop with name, type, and optional default.
Declares the Rust constructor expression (native_widget only).
Declares the path to the Rust crate (native_widget only).
Declares internal state fields for a stateful widget.
State fields are managed by the runtime, not the app model.
They persist across renders and are passed to view/3 and
handle_event/2. Declaring state fields makes the widget
stateful: the view is deferred to tree normalization and the
WidgetHandler behaviour is injected automatically.
state hover: nil, drag: :none, animation_progress: 0.0
Converts a widget struct to a ui_node() map via the WidgetProtocol.
Declares the widget type name. Pass container: true for container widgets.