whiskers_widgets/
lib.rs

1//! # Overview
2//!
3//! This crate implements the UI widget machinery used by the `whiskers` crate to display the sketch
4//! parameter UI. The main entry point is the [`trait@Widget`] trait and the associated macros defined
5//! in [`whiskers-derive`].
6//!
7//! This crate is separate from the main crate to allow other crates (including the `vsvg` crate)
8//! to implement the [`trait@Widget`] trait for their own types.
9//!
10//! # Implementing the `Widget` trait
11//!
12//! For each supported sketch parameter type `T`, there must exist a widget type that implements
13//! [`Widget<T>`] and is registered with [`register_widget_ui!`] macro. This module include
14//! the traits and macros needed to support this mechanism, as well as widgets for basic types.
15//!
16//! For example, let's consider the [`prim@bool`] type:
17//!
18//! ```ignore
19//! #[derive(Default)]
20//! pub struct BoolWidget;
21//!
22//! impl whiskers_widgets::Widget<bool> for BoolWidget {
23//!     fn ui(&self, ui: &mut egui::Ui, label: &str, value: &mut bool) -> egui::Response {
24//!         ui.horizontal(|_| {});
25//!         ui.checkbox(value, label)
26//!     }
27//! }
28//!
29//! whiskers_widgets::register_widget_ui!(bool, BoolWidget);
30//! ```
31//!
32//! The [`BoolWidget`] type implements the [`Widget<bool>`] trait, which requires the [`Widget::ui`]
33//! method, and is registered with the [`crate::register_widget_ui!`] macro. Note that the
34//! [`Widget::ui`] method is called in the context of an 2-column [`egui::Grid`], so it must contain
35//! exactly two top level UI calls, where the first one typically is the label, and the second the
36//! actual interactive widget. In the case of a checkbox, the label is already embedded in the UI
37//! widget, we leave the first column empty.
38//!
39//! The [`BoolWidget`] type is already provided by the [`crate`] crate, but custom widgets can be
40//! implemented for custom types using the same pattern.
41//!
42//! # Configuring widgets
43//!
44//! Many widgets support additional configuration options, which can be set using the
45//! `#[param(...)]` attribute of the [`whiskers_derive::Sketch`] macro. This is done by using the
46//! builder pattern on the widget type. For example, here is an extract of [`NumericWidget`], which
47//! supports numerical types such as [`f64`] and [`i32`]:
48//!
49//! ```ignore
50//! # use egui::emath::Numeric;
51//! # use core::f64;
52//!
53//! #[derive(Default)]
54//! pub struct NumericWidget<T: Numeric> {
55//!     step: Option<T>,
56//!     slider: bool,
57//!     /* ... */
58//! }
59//!
60//! impl<T: Numeric> NumericWidget<T> {
61//!     pub fn step(mut self, step: T) -> Self {
62//!         self.step = Some(step);
63//!         self
64//!     }
65//!
66//!     pub fn slider(mut self, slider: bool) -> Self {
67//!         self.slider = slider;
68//!         self
69//!     }
70//! }
71//!
72//! impl<T: Numeric> whiskers_widgets::Widget<T> for NumericWidget<T> {
73//!     /* ... */
74//! #    fn ui(&self, ui: &mut egui::Ui, label: &str, value: &mut T) -> bool { todo!(); }
75//! }
76//!
77//! whiskers_widgets::register_widget_ui!(f64, NumericWidget<f64>);
78//! /* ... */
79//!
80//! # fn main() {}
81//! ```
82//! Now let's consider a hypothetical sketch:
83//! ```ignore
84//! #[sketch_app]
85//! #[derive(Default)]
86//! struct MySketch {
87//!     #[param(slider, step = 0.1)]
88//!     irregularity: f64,
89//! }
90//! ```
91//! Based on the `#[param(...)]` attributes, the [`whiskers_derive::Sketch`] derive macro will
92//! automatically generate the corresponding builder pattern calls:
93//! ```rust
94//! # #[allow(unused_must_use)]
95//! # fn main() {
96//! whiskers_widgets::NumericWidget::<f64>::default().slider(true).step(0.1);
97//! # }
98//! ```
99//! Note that when no value is provided for a key (such as `slider` here), a boolean value of
100//! [`true`] is assumed.
101
102mod bool;
103mod numeric;
104mod string;
105mod ui;
106mod vec;
107
108pub use bool::*;
109pub use numeric::*;
110pub use string::*;
111pub use ui::*;
112pub use vec::*;
113
114// reexport whiskers-derive macros
115pub use whiskers_derive::{sketch_app, sketch_widget, Sketch, Widget};
116
117/// Exported dependencies, for use by whiskers_derive
118pub mod exports {
119    pub use egui;
120    pub use serde;
121}
122
123pub trait WidgetApp {
124    /// The name of the sketch, used the window title, the default output file name, and persistent
125    /// settings.
126    fn name(&self) -> String;
127
128    /// Draw the UI for the sketch, return whether the sketch should be updated.
129    ///
130    /// This function is generated by the [`Sketch`] derive macro.
131    fn ui(&mut self, ui: &mut egui::Ui) -> bool;
132}
133
134/// This is the base trait for widgets used to display sketch parameters in the UI.
135///
136/// For each supported sketch parameter type `T`, there must exist an implementation of
137/// [`Widget<T>`] that is furthermore registered using the [`crate::register_widget_ui!`] macro.
138pub trait Widget<T> {
139    /// This function implements the actual UI for the widget.
140    ///
141    /// Returns [`true`] if the value was changed.
142    fn ui(&self, ui: &mut egui::Ui, label: &str, value: &mut T) -> bool;
143
144    /// Indicates whether the widget should be displayed in a two-column grid.
145    ///
146    /// Complex and/or compound widgets which cannot fit the two-column grid layout should return
147    /// [`false`].
148    fn use_grid() -> bool {
149        true
150    }
151}
152
153/// This utility trait serves to associate a [`Widget`] type with a given sketch parameter type `T`.
154///
155/// Do not implement this trait manually, instead use the [`crate::register_widget_ui!`] macro.
156pub trait WidgetMapper<T> {
157    /// [`Widget`] type associated with the sketch parameter type `T`.
158    type Type: Widget<T>;
159}
160
161/// Registers a given [`Widget`] type for given sketch parameter type.
162///
163/// This is a convenience macro that implements the [`WidgetMapper`] trait for the given types.
164///
165/// # Example
166///
167/// ```ignore
168/// register_widget_ui!(bool, BoolWidget);
169/// ```
170#[macro_export]
171macro_rules! register_widget_ui {
172    ($t: ty, $ui: ty) => {
173        impl $crate::WidgetMapper<$t> for $t {
174            type Type = $ui;
175        }
176    };
177}