registration.rs
raw
//! The open widget registry: how a widget an application wrote in Rust makes
//! itself constructible by name.
//!
//! A widget crate declares its interface in a `.gdw` manifest and registers the
//! Rust side from it with [`register_widget!`](crate::register_widget), which
//! emits an [`inventory::submit!`] of a [`WidgetRegistration`]. The framework's
//! own builtins register the same way (from their descriptors). Every
//! registration linked into the binary is collected here, so the interpreter
//! builds a widget it has no compile-time knowledge of — a `container` and a
//! `markdown` through one mechanism, sourced from the link.
//!
//! The registration is fn-pointer-based, so an `inventory::submit!` item can be
//! the `Sync` static that mechanism requires — it holds no heap handle.
use std::any::Any;
use crate::widget::Widget;
/// One property a registered widget can be given: the name a `.gdc` writes and
/// a thunk that applies a boxed value through the widget's setter.
///
/// The thunk is generated with the concrete widget and value types in hand, so
/// the setter call it makes is type-checked by rustc — a manifest that names a
/// setter the widget does not have, or the wrong value type for it, is a
/// compile error at the generated call, not a silent runtime miss.
pub struct SetterEntry {
/// The keyword a `.gdc` writes (`corner-radius` → `:corner-radius`).
pub prop: &'static str,
/// Apply a boxed value to a widget. A widget or value that is not what the
/// thunk expects applies nothing — the erased spelling of the mismatch that
/// makes a mistargeted `Commands::mutate` a no-op.
pub apply: fn(&mut dyn Widget, Box<dyn Any>),
}
/// Everything the runtime needs to build one registered widget and drive its
/// properties: its `.gdc`/theme name, a constructor, and a setter per property.
///
/// One is submitted per [`register_widget!`](crate::register_widget); the whole
/// linked set is [`registered_widgets`].
pub struct WidgetRegistration {
/// The name a `.gdc` node and a theme selector write.
pub name: &'static str,
/// Build a fresh widget. Registered widgets are `Default`, so this is
/// always `|| Box::new(T::default())`.
pub construct: fn() -> Box<dyn Widget>,
/// One [`SetterEntry`] per property the manifest declared.
pub setters: &'static [SetterEntry],
}
inventory::collect!(WidgetRegistration);
/// Every widget registered with `register_widget!` across the linked crates.
pub fn registered_widgets() -> impl Iterator<Item = &'static WidgetRegistration> {
inventory::iter::<WidgetRegistration>()
}
/// The registration of one widget by name, if it is linked in.
pub fn registered_widget(name: &str) -> Option<&'static WidgetRegistration> {
registered_widgets().find(|registration| registration.name == name)
}