plushie/native_widget
Native widget system for custom Rust-backed widget types.
Plushie supports two kinds of custom widgets:
-
Native widgets: backed by a Rust crate implementing the
PlushieWidgettrait. The crate is compiled into the plushie binary, and the widget communicates via the standard wire protocol. -
Composite widgets: pure Gleam widgets that compose built-in widgets into reusable components. They produce standard Node trees and require no Rust code.
Native widgets
A native widget is defined by creating a NativeDef and
registering it. The widget’s Rust crate handles rendering and
event emission; the Gleam side provides the typed builder API.
import plushie/native_widget
import plushie/node.{type Node}
import plushie/prop/color
import plushie/prop/length
// Define the native widget
pub const gauge_def = native_widget.NativeDef(
kind: "gauge",
rust_crate: "native/my_gauge",
rust_constructor: "my_gauge::GaugeExtension::new()",
props: [
native_widget.NumberProp("value"),
native_widget.NumberProp("min"),
native_widget.NumberProp("max"),
native_widget.ColorProp("color"),
native_widget.LengthProp("width"),
],
commands: [
native_widget.CommandDef("set_value", [native_widget.NumberParam("value")]),
],
)
// Build a gauge widget
pub fn gauge(id: String, value: Float, opts: List(GaugeOpt)) -> Node {
native_widget.build(gauge_def, id, [
#("value", node.FloatVal(value)),
..gauge_opts_to_props(opts)
])
}
// Send a command to a gauge
pub fn set_gauge_value(node_id: String, value: Float) -> Command(msg) {
native_widget.command(gauge_def, node_id, "set_value", [
#("value", node.FloatVal(value)),
])
}
Composite widgets
Composite widgets are simpler: they’re just functions that return Node trees. No registration or Rust code needed.
import plushie/node.{type Node}
import plushie/ui
import plushie/prop/padding
// A labeled input composite widget
pub fn labeled_input(
id: String,
label: String,
value: String,
children: List(Node),
) -> Node {
ui.column(id, [column.Spacing(4)], [
ui.text_(id <> "-label", label),
ui.text_input(id <> "-input", value, []),
..children
])
}
Types
Definition of a native widget.
Describes the Rust crate, constructor, props, and commands that a native widget supports. Used at compile time to configure the plushie binary build and at runtime to construct nodes and commands.
pub type NativeDef {
NativeDef(
kind: String,
rust_crate: String,
rust_constructor: String,
props: List(PropDef),
commands: List(CommandDef),
)
}
Constructors
-
NativeDef( kind: String, rust_crate: String, rust_constructor: String, props: List(PropDef), commands: List(CommandDef), )Arguments
- kind
-
Widget kind string (e.g., “gauge”). Must match the Rust crate’s registered widget type name.
- rust_crate
-
Path to the Rust crate relative to the project root (e.g., “native/my_gauge”).
- rust_constructor
-
Rust expression to construct the widget instance (e.g., “my_gauge::GaugeExtension::new()”).
- props
-
Declared properties with their types, for documentation and build tooling validation.
- commands
-
Declared commands that can be sent to this widget type.
Parameter definition for native widget commands.
pub type ParamDef {
NumberParam(name: String)
StringParam(name: String)
BooleanParam(name: String)
}
Constructors
-
NumberParam(name: String) -
StringParam(name: String) -
BooleanParam(name: String)
Property definition for a native widget.
pub type PropDef {
NumberProp(name: String)
StringProp(name: String)
BooleanProp(name: String)
ColorProp(name: String)
LengthProp(name: String)
PaddingProp(name: String)
AlignmentProp(name: String)
FontProp(name: String)
StyleProp(name: String)
MapProp(name: String)
AnyProp(name: String)
ListProp(name: String, inner: String)
}
Constructors
-
NumberProp(name: String) -
StringProp(name: String) -
BooleanProp(name: String) -
ColorProp(name: String) -
LengthProp(name: String) -
PaddingProp(name: String) -
AlignmentProp(name: String) -
FontProp(name: String) -
StyleProp(name: String) -
MapProp(name: String) -
AnyProp(name: String) -
ListProp(name: String, inner: String)
Values
pub fn build(
def: NativeDef,
id: String,
props: List(#(String, node.PropValue)),
) -> node.Node
Build a node for a native widget.
Creates a Node with the native widget’s kind and the given props. Props are passed as key-value pairs already encoded to PropValue.
pub fn build_container(
def: NativeDef,
id: String,
props: List(#(String, node.PropValue)),
children: List(node.Node),
) -> node.Node
Build a container node for a native widget with children.
pub fn command(
def: NativeDef,
node_id: String,
op: String,
payload: List(#(String, node.PropValue)),
) -> command.Command(msg)
Create a native widget command targeting a specific widget instance.
The command is sent via the wire protocol’s unified command
message type and delivered to the Rust widget by node ID.
pub fn command_names(def: NativeDef) -> List(String)
Get the command definition names from a native widget definition.
pub fn commands(
def: NativeDef,
cmds: List(#(String, String, List(#(String, node.PropValue)))),
) -> command.Command(msg)
Create a batch of native widget commands.
pub fn prop_names(def: NativeDef) -> List(String)
Get the prop definition names from a native widget definition.
pub fn validate(def: NativeDef) -> Result(Nil, List(String))
Validate a native widget definition at runtime.
Returns Ok(Nil) when valid, or Error(errors) with a list of
human-readable validation failure messages.
Checks performed:
kindmust be non-empty- No duplicate prop names
- No reserved prop names (id, type, children, a11y)
- Command names must be safe native operation names