teksilo_preview/doc_snippet.rs
1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Documentation snippets — a one-line registration for widgets that
5//! want a picture in the generated mdBook catalog without carrying a
6//! full [`WidgetCatalog`](crate::WidgetCatalog) impl.
7//!
8//! A catalog entry is a *live* previewer subject: it declares an id, a
9//! group, typed knobs, and a set of variants, because the previewer GUI
10//! builds an editing form out of them. A documentation image needs none
11//! of that — one representative instance is the whole requirement. So a
12//! widget that would otherwise go unpictured registers a snippet:
13//!
14//! ```ignore
15//! use teksilo_preview::doc_snippet;
16//!
17//! doc_snippet!("crates/teksilo-widgets/src/banner.rs", {
18//! Box::new(Banner::info(lit!("Your trial ends in 3 days.")))
19//! });
20//! ```
21//!
22//! The exporter keys images by the **source file** (its stem is the
23//! catalog page's slug), so the path decides which documentation page
24//! the image lands on — exactly like
25//! [`register_widget_catalog_at!`](crate::register_widget_catalog_at).
26//!
27//! A widget that fills whatever space it is given (a data view, a
28//! docking layout, a scroll area) reports no useful intrinsic size, so
29//! it pins the canvas:
30//!
31//! ```ignore
32//! doc_snippet!("crates/teksilo-widgets/src/table_view.rs", size = (640.0, 240.0), {
33//! Box::new(build_sample_table())
34//! });
35//! ```
36//!
37//! Snippets take precedence over a catalog entry for the same file: the
38//! catalog's default variant is chosen for the previewer's benefit, and
39//! a few of them (`Spacer`, `Expand`) paint nothing at all.
40
41use teksilo_core::widget::Widget;
42
43/// One registered documentation image subject.
44pub struct DocSnippet {
45 /// Workspace-relative path of the widget's own source file. Its stem
46 /// is the catalog page slug the image is filed under.
47 pub source_file: &'static str,
48 /// Constructs a fresh instance. A plain `fn` (not a closure) so the
49 /// whole record is a `const`-constructible static.
50 pub build: fn() -> Box<dyn Widget>,
51 /// Pin the canvas to this logical size instead of measuring the
52 /// widget's intrinsic size. For widgets that fill their parent.
53 pub size: Option<(f32, f32)>,
54}
55
56inventory::collect!(DocSnippet);
57
58/// Iterate every documentation snippet registered into the current
59/// binary's link graph.
60pub fn iter_doc_snippets() -> impl Iterator<Item = &'static DocSnippet> {
61 inventory::iter::<DocSnippet>()
62}
63
64/// Register a documentation image subject for a widget source file.
65///
66/// ```ignore
67/// doc_snippet!("crates/teksilo-widgets/src/banner.rs", { Box::new(Banner::info(lit!("Hi"))) });
68/// doc_snippet!("crates/teksilo-widgets/src/log_view.rs", size = (620.0, 220.0), { Box::new(v) });
69/// ```
70#[macro_export]
71macro_rules! doc_snippet {
72 ($file:literal, size = ($w:expr, $h:expr), $build:block) => {
73 $crate::__doc_snippet_with!($file, ::std::option::Option::Some(($w, $h)), $build);
74 };
75 ($file:literal, $build:block) => {
76 $crate::__doc_snippet_with!($file, ::std::option::Option::None, $build);
77 };
78}
79
80#[doc(hidden)]
81#[macro_export]
82macro_rules! __doc_snippet_with {
83 ($file:literal, $size:expr, $build:block) => {
84 const _: () = {
85 fn __build() -> ::std::boxed::Box<dyn $crate::__widget::Widget> {
86 $build
87 }
88 $crate::__inventory::submit! {
89 $crate::DocSnippet {
90 source_file: $file,
91 build: __build,
92 size: $size,
93 }
94 }
95 };
96 };
97}