//! 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), } /// 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, /// 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 { inventory::iter::() } /// 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) }