Skip to main content

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}