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

mod bool;
mod numeric;
mod string;
mod ui;

pub use bool::*;
pub use numeric::*;
pub use string::*;
pub use ui::*;

// reexport whiskers-derive macros
pub use whiskers_derive::{sketch_app, sketch_widget, Sketch, Widget};

/// Exported dependencies, for use by whiskers_derive
pub mod exports {
    pub use egui;
    pub use serde;
}

pub trait WidgetApp {
    /// The name of the sketch, used the window title, the default output file name, and persistent
    /// settings.
    fn name(&self) -> String;

    /// Draw the UI for the sketch, return whether the sketch should be updated.
    ///
    /// This function is generated by the [`Sketch`] derive macro.
    fn ui(&mut self, ui: &mut egui::Ui) -> bool;
}

/// This is the base trait for widgets used to display sketch parameters in the UI.
///
/// For each supported sketch parameter type `T`, there must exist an implementation of
/// [`Widget<T>`] that is furthermore registered using the [`crate::register_widget_ui!`] macro.
pub trait Widget<T> {
    /// This function implements the actual UI for the widget.
    ///
    /// Returns [`true`] if the value was changed.
    fn ui(&self, ui: &mut egui::Ui, label: &str, value: &mut T) -> bool;

    /// Indicates whether the widget should be displayed in a two-column grid.
    ///
    /// Complex and/or compound widgets which cannot fit the two-column grid layout should return
    /// [`false`].
    fn use_grid() -> bool {
        true
    }
}

/// This utility trait serves to associate a [`Widget`] type with a given sketch parameter type `T`.
///
/// Do not implement this trait manually, instead use the [`crate::register_widget_ui!`] macro.
pub trait WidgetMapper<T> {
    /// [`Widget`] type associated with the sketch parameter type `T`.
    type Type: Widget<T>;
}

/// Registers a given [`Widget`] type for given sketch parameter type.
///
/// This is a convenience macro that implements the [`WidgetMapper`] trait for the given types.
///
/// # Example
///
/// ```ignore
/// register_widget_ui!(bool, BoolWidget);
/// ```
#[macro_export]
macro_rules! register_widget_ui {
    ($t: ty, $ui: ty) => {
        impl $crate::WidgetMapper<$t> for $t {
            type Type = $ui;
        }
    };
}